mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 2332-mark-contribution-as-rejected
This commit is contained in:
commit
c84fa68132
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -437,7 +437,7 @@ jobs:
|
|||||||
report_name: Coverage Frontend
|
report_name: Coverage Frontend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./coverage/lcov.info
|
||||||
min_coverage: 91
|
min_coverage: 92
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
52
CHANGELOG.md
52
CHANGELOG.md
@ -4,8 +4,60 @@ All notable changes to this project will be documented in this file. Dates are d
|
|||||||
|
|
||||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||||
|
|
||||||
|
#### [1.17.0](https://github.com/gradido/gradido/compare/1.16.0...1.17.0)
|
||||||
|
|
||||||
|
- fix(frontend): submit contribution text [`#2573`](https://github.com/gradido/gradido/pull/2573)
|
||||||
|
- fix(backend): admin cannot delete confirmed contribution [`#2571`](https://github.com/gradido/gradido/pull/2571)
|
||||||
|
- fix(frontend): english locales - horas -> hours [`#2572`](https://github.com/gradido/gradido/pull/2572)
|
||||||
|
- fix(frontend): mobil divices datepicker add props dropleft [`#2570`](https://github.com/gradido/gradido/pull/2570)
|
||||||
|
- fix(frontend): pagination [`#2569`](https://github.com/gradido/gradido/pull/2569)
|
||||||
|
- fix(frontend): add a watch on gdt prop to assure propper loading when mounted [`#2568`](https://github.com/gradido/gradido/pull/2568)
|
||||||
|
- refactor(frontend): creation step in quarter hour set [`#2566`](https://github.com/gradido/gradido/pull/2566)
|
||||||
|
- fix(frontend): tunneled email on right side last transactions [`#2561`](https://github.com/gradido/gradido/pull/2561)
|
||||||
|
- feat(frontend): test transaction page [`#2555`](https://github.com/gradido/gradido/pull/2555)
|
||||||
|
- refactor(backend): statistics with field resolvers [`#2553`](https://github.com/gradido/gradido/pull/2553)
|
||||||
|
- fix(frontend): normalized amount transaction if processed again [`#2550`](https://github.com/gradido/gradido/pull/2550)
|
||||||
|
- fix(backend): semaphore deadlock [`#2551`](https://github.com/gradido/gradido/pull/2551)
|
||||||
|
- fix(frontend): mobile design [`#2552`](https://github.com/gradido/gradido/pull/2552)
|
||||||
|
- refactor(frontend): slots for right sidebar and header [`#2548`](https://github.com/gradido/gradido/pull/2548)
|
||||||
|
- fix(frontend): creation menu highlighted on all submenus [`#2527`](https://github.com/gradido/gradido/pull/2527)
|
||||||
|
- refactor(frontend): computed hours for open creations [`#2545`](https://github.com/gradido/gradido/pull/2545)
|
||||||
|
- feat(other): add description for daily backup cronjob [`#2532`](https://github.com/gradido/gradido/pull/2532)
|
||||||
|
- fix(frontend): editing transaction does not work [`#2543`](https://github.com/gradido/gradido/pull/2543)
|
||||||
|
- refactor(frontend): remove open creations from store [`#2541`](https://github.com/gradido/gradido/pull/2541)
|
||||||
|
- feat(other): vscode extensions [`#2524`](https://github.com/gradido/gradido/pull/2524)
|
||||||
|
- fix(backend): remove jest from dependecies [`#2533`](https://github.com/gradido/gradido/pull/2533)
|
||||||
|
- fix(frontend): initials without space [`#2546`](https://github.com/gradido/gradido/pull/2546)
|
||||||
|
- fix(backend): fix backend not confirmable [`#2539`](https://github.com/gradido/gradido/pull/2539)
|
||||||
|
- fix(frontend): send gdd and send link gdd is running [`#2534`](https://github.com/gradido/gradido/pull/2534)
|
||||||
|
- fix(other): update browser list [`#2540`](https://github.com/gradido/gradido/pull/2540)
|
||||||
|
- test(backend): increase backend coverage to 78% [`#2542`](https://github.com/gradido/gradido/pull/2542)
|
||||||
|
- fix(frontend): pagination gdt [`#2525`](https://github.com/gradido/gradido/pull/2525)
|
||||||
|
- fix(frontend): leaves are over the user symbol [`#2526`](https://github.com/gradido/gradido/pull/2526)
|
||||||
|
- fix(frontend): input-email label and placeholder are displayed correctly per language [`#2528`](https://github.com/gradido/gradido/pull/2528)
|
||||||
|
- feat(backend): add hideAmountGDD & hideAmountGDT to users table. [`#2506`](https://github.com/gradido/gradido/pull/2506)
|
||||||
|
- fix(frontend): avatar initials always has 2 letters [`#2530`](https://github.com/gradido/gradido/pull/2530)
|
||||||
|
- refactor(backend): seed contributions as user [`#2460`](https://github.com/gradido/gradido/pull/2460)
|
||||||
|
- fix(backend): fix logger middleware [`#2503`](https://github.com/gradido/gradido/pull/2503)
|
||||||
|
- fix(backend): fix email text [`#2523`](https://github.com/gradido/gradido/pull/2523)
|
||||||
|
- feat(other): new scopes for lint pr [`#2489`](https://github.com/gradido/gradido/pull/2489)
|
||||||
|
- fix(backend): fix config - some typos [`#2477`](https://github.com/gradido/gradido/pull/2477)
|
||||||
|
- style(frontend): new Design [`#2297`](https://github.com/gradido/gradido/pull/2297)
|
||||||
|
- refactor(other): adjust some texts and translations [`#2504`](https://github.com/gradido/gradido/pull/2504)
|
||||||
|
- test(other): fix tests breaking with the new year [`#2505`](https://github.com/gradido/gradido/pull/2505)
|
||||||
|
- feat(backend): federation implement exchange of api versions persist in table [`#2427`](https://github.com/gradido/gradido/pull/2427)
|
||||||
|
- feat(backend): semaphore to lock transaction table [`#2458`](https://github.com/gradido/gradido/pull/2458)
|
||||||
|
- feat(backend): design html emails and adjust texts [`#2472`](https://github.com/gradido/gradido/pull/2472)
|
||||||
|
- feat(backend): test semaphore [`#2468`](https://github.com/gradido/gradido/pull/2468)
|
||||||
|
- fix(admin): reduce triggers of success toast on deleted user form to exactly one [`#2471`](https://github.com/gradido/gradido/pull/2471)
|
||||||
|
- refactor(other): build nginx docker image in workflow independent of other builds [`#2470`](https://github.com/gradido/gradido/pull/2470)
|
||||||
|
- feat(backend): setup unit tests for federation [`#2465`](https://github.com/gradido/gradido/pull/2465)
|
||||||
|
|
||||||
#### [1.16.0](https://github.com/gradido/gradido/compare/1.15.0...1.16.0)
|
#### [1.16.0](https://github.com/gradido/gradido/compare/1.15.0...1.16.0)
|
||||||
|
|
||||||
|
> 15 December 2022
|
||||||
|
|
||||||
|
- feat(release): version 1.16.0 [`#2467`](https://github.com/gradido/gradido/pull/2467)
|
||||||
- refactor(backend): cleaning user related old password junk [`#2426`](https://github.com/gradido/gradido/pull/2426)
|
- refactor(backend): cleaning user related old password junk [`#2426`](https://github.com/gradido/gradido/pull/2426)
|
||||||
- fix(database): consistent transaction table [`#2453`](https://github.com/gradido/gradido/pull/2453)
|
- fix(database): consistent transaction table [`#2453`](https://github.com/gradido/gradido/pull/2453)
|
||||||
- refactor(backend): dissolve admin resolver [`#2416`](https://github.com/gradido/gradido/pull/2416)
|
- refactor(backend): dissolve admin resolver [`#2416`](https://github.com/gradido/gradido/pull/2416)
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"description": "Administraion Interface for Gradido",
|
"description": "Administraion Interface for Gradido",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "Moriz Wahl",
|
"author": "Moriz Wahl",
|
||||||
"version": "1.16.0",
|
"version": "1.17.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -4,12 +4,14 @@ export const communityStatistics = gql`
|
|||||||
query {
|
query {
|
||||||
communityStatistics {
|
communityStatistics {
|
||||||
totalUsers
|
totalUsers
|
||||||
activeUsers
|
|
||||||
deletedUsers
|
deletedUsers
|
||||||
totalGradidoCreated
|
totalGradidoCreated
|
||||||
totalGradidoDecayed
|
totalGradidoDecayed
|
||||||
|
dynamicStatisticsFields {
|
||||||
|
activeUsers
|
||||||
totalGradidoAvailable
|
totalGradidoAvailable
|
||||||
totalGradidoUnbookedDecayed
|
totalGradidoUnbookedDecayed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -17,13 +17,15 @@ const defaultData = () => {
|
|||||||
return {
|
return {
|
||||||
communityStatistics: {
|
communityStatistics: {
|
||||||
totalUsers: 3113,
|
totalUsers: 3113,
|
||||||
activeUsers: 1057,
|
|
||||||
deletedUsers: 35,
|
deletedUsers: 35,
|
||||||
totalGradidoCreated: '4083774.05000000000000000000',
|
totalGradidoCreated: '4083774.05000000000000000000',
|
||||||
totalGradidoDecayed: '-1062639.13634129622923372197',
|
totalGradidoDecayed: '-1062639.13634129622923372197',
|
||||||
|
dynamicStatisticsFields: {
|
||||||
|
activeUsers: 1057,
|
||||||
totalGradidoAvailable: '2513565.869444365732411569',
|
totalGradidoAvailable: '2513565.869444365732411569',
|
||||||
totalGradidoUnbookedDecayed: '-500474.6738366222166261272',
|
totalGradidoUnbookedDecayed: '-500474.6738366222166261272',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,9 @@ export default {
|
|||||||
return communityStatistics
|
return communityStatistics
|
||||||
},
|
},
|
||||||
update({ communityStatistics }) {
|
update({ communityStatistics }) {
|
||||||
this.statistics = communityStatistics
|
const totals = { ...communityStatistics.dynamicStatisticsFields }
|
||||||
|
this.statistics = { ...communityStatistics, ...totals }
|
||||||
|
delete this.statistics.dynamicStatisticsFields
|
||||||
},
|
},
|
||||||
error({ message }) {
|
error({ message }) {
|
||||||
this.toastError(message)
|
this.toastError(message)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido-backend",
|
"name": "gradido-backend",
|
||||||
"version": "1.16.0",
|
"version": "1.17.0",
|
||||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": "https://github.com/gradido/gradido/backend",
|
"repository": "https://github.com/gradido/gradido/backend",
|
||||||
|
|||||||
@ -2,13 +2,25 @@ import { ObjectType, Field } from 'type-graphql'
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class CommunityStatistics {
|
export class DynamicStatisticsFields {
|
||||||
@Field(() => Number)
|
|
||||||
totalUsers: number
|
|
||||||
|
|
||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
activeUsers: number
|
activeUsers: number
|
||||||
|
|
||||||
|
@Field(() => Decimal)
|
||||||
|
totalGradidoAvailable: Decimal
|
||||||
|
|
||||||
|
@Field(() => Decimal)
|
||||||
|
totalGradidoUnbookedDecayed: Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class CommunityStatistics {
|
||||||
|
@Field(() => Number)
|
||||||
|
allUsers: number
|
||||||
|
|
||||||
|
@Field(() => Number)
|
||||||
|
totalUsers: number
|
||||||
|
|
||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
deletedUsers: number
|
deletedUsers: number
|
||||||
|
|
||||||
@ -18,9 +30,7 @@ export class CommunityStatistics {
|
|||||||
@Field(() => Decimal)
|
@Field(() => Decimal)
|
||||||
totalGradidoDecayed: Decimal
|
totalGradidoDecayed: Decimal
|
||||||
|
|
||||||
@Field(() => Decimal)
|
// be carefull querying this, takes longer than 2 secs.
|
||||||
totalGradidoAvailable: Decimal
|
@Field(() => DynamicStatisticsFields)
|
||||||
|
dynamicStatisticsFields: DynamicStatisticsFields
|
||||||
@Field(() => Decimal)
|
|
||||||
totalGradidoUnbookedDecayed: Decimal
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||||
|
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||||
import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
||||||
import {
|
import {
|
||||||
@ -26,7 +27,13 @@ import {
|
|||||||
sendContributionConfirmedEmail,
|
sendContributionConfirmedEmail,
|
||||||
// sendContributionRejectedEmail,
|
// sendContributionRejectedEmail,
|
||||||
} from '@/emails/sendEmailVariants'
|
} from '@/emails/sendEmailVariants'
|
||||||
import { cleanDB, resetToken, testEnvironment, contributionDateFormatter } from '@test/helpers'
|
import {
|
||||||
|
cleanDB,
|
||||||
|
resetToken,
|
||||||
|
testEnvironment,
|
||||||
|
contributionDateFormatter,
|
||||||
|
resetEntity,
|
||||||
|
} from '@test/helpers'
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import { userFactory } from '@/seeds/factory/user'
|
import { userFactory } from '@/seeds/factory/user'
|
||||||
import { creationFactory } from '@/seeds/factory/creation'
|
import { creationFactory } from '@/seeds/factory/creation'
|
||||||
@ -1818,6 +1825,49 @@ describe('ContributionResolver', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('creation already confirmed', () => {
|
||||||
|
it('throws an error', async () => {
|
||||||
|
await userFactory(testEnv, bobBaumeister)
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'bob@baumeister.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
data: { createContribution: confirmedContribution },
|
||||||
|
} = await mutate({
|
||||||
|
mutation: createContribution,
|
||||||
|
variables: {
|
||||||
|
amount: 100.0,
|
||||||
|
memo: 'Confirmed Contribution',
|
||||||
|
creationDate: contributionDateFormatter(new Date()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
await mutate({
|
||||||
|
mutation: confirmContribution,
|
||||||
|
variables: {
|
||||||
|
id: confirmedContribution.id ? confirmedContribution.id : -1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: adminDeleteContribution,
|
||||||
|
variables: {
|
||||||
|
id: confirmedContribution.id ? confirmedContribution.id : -1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('A confirmed contribution can not be deleted')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
await resetEntity(DbTransaction)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('confirmContribution', () => {
|
describe('confirmContribution', () => {
|
||||||
|
|||||||
@ -513,6 +513,10 @@ export class ContributionResolver {
|
|||||||
logger.error(`Contribution not found for given id: ${id}`)
|
logger.error(`Contribution not found for given id: ${id}`)
|
||||||
throw new Error('Contribution not found for given id.')
|
throw new Error('Contribution not found for given id.')
|
||||||
}
|
}
|
||||||
|
if (contribution.confirmedAt) {
|
||||||
|
logger.error('A confirmed contribution can not be deleted')
|
||||||
|
throw new Error('A confirmed contribution can not be deleted')
|
||||||
|
}
|
||||||
const moderator = getUser(context)
|
const moderator = getUser(context)
|
||||||
if (
|
if (
|
||||||
contribution.contributionType === ContributionType.USER &&
|
contribution.contributionType === ContributionType.USER &&
|
||||||
@ -558,7 +562,6 @@ export class ContributionResolver {
|
|||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
// acquire lock
|
// acquire lock
|
||||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||||
const contribution = await DbContribution.findOne(id)
|
const contribution = await DbContribution.findOne(id)
|
||||||
@ -669,7 +672,6 @@ export class ContributionResolver {
|
|||||||
} finally {
|
} finally {
|
||||||
releaseLock()
|
releaseLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,33 +1,82 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { Resolver, Query, Authorized } from 'type-graphql'
|
import { Resolver, Query, Authorized, FieldResolver } from 'type-graphql'
|
||||||
import { getConnection } from '@dbTools/typeorm'
|
import { getConnection } from '@dbTools/typeorm'
|
||||||
|
|
||||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
import { User as DbUser } from '@entity/User'
|
import { User as DbUser } from '@entity/User'
|
||||||
|
|
||||||
import { CommunityStatistics } from '@model/CommunityStatistics'
|
import { CommunityStatistics, DynamicStatisticsFields } from '@model/CommunityStatistics'
|
||||||
|
|
||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
import { calculateDecay } from '@/util/decay'
|
import { calculateDecay } from '@/util/decay'
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
@Resolver((of) => CommunityStatistics)
|
||||||
|
|
||||||
@Resolver()
|
|
||||||
export class StatisticsResolver {
|
export class StatisticsResolver {
|
||||||
@Authorized([RIGHTS.COMMUNITY_STATISTICS])
|
@Authorized([RIGHTS.COMMUNITY_STATISTICS])
|
||||||
@Query(() => CommunityStatistics)
|
@Query(() => CommunityStatistics)
|
||||||
async communityStatistics(): Promise<CommunityStatistics> {
|
async communityStatistics(): Promise<CommunityStatistics> {
|
||||||
const allUsers = await DbUser.count({ withDeleted: true })
|
return new CommunityStatistics()
|
||||||
const totalUsers = await DbUser.count()
|
}
|
||||||
const deletedUsers = allUsers - totalUsers
|
|
||||||
|
|
||||||
|
@FieldResolver(() => Decimal)
|
||||||
|
async allUsers(): Promise<number> {
|
||||||
|
return await DbUser.count({ withDeleted: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async totalUsers(): Promise<number> {
|
||||||
|
return await DbUser.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async deletedUsers(): Promise<number> {
|
||||||
|
return (await this.allUsers()) - (await this.totalUsers())
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async totalGradidoCreated(): Promise<Decimal> {
|
||||||
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
|
try {
|
||||||
|
await queryRunner.connect()
|
||||||
|
const { totalGradidoCreated } = await queryRunner.manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.select('SUM(transaction.amount) AS totalGradidoCreated')
|
||||||
|
.from(DbTransaction, 'transaction')
|
||||||
|
.where('transaction.typeId = 1')
|
||||||
|
.getRawOne()
|
||||||
|
return totalGradidoCreated
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async totalGradidoDecayed(): Promise<Decimal> {
|
||||||
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
|
try {
|
||||||
|
await queryRunner.connect()
|
||||||
|
const { totalGradidoDecayed } = await queryRunner.manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.select('SUM(transaction.decay) AS totalGradidoDecayed')
|
||||||
|
.from(DbTransaction, 'transaction')
|
||||||
|
.where('transaction.decay IS NOT NULL')
|
||||||
|
.getRawOne()
|
||||||
|
return totalGradidoDecayed
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async dynamicStatisticsFields(): Promise<DynamicStatisticsFields> {
|
||||||
let totalGradidoAvailable: Decimal = new Decimal(0)
|
let totalGradidoAvailable: Decimal = new Decimal(0)
|
||||||
let totalGradidoUnbookedDecayed: Decimal = new Decimal(0)
|
let totalGradidoUnbookedDecayed: Decimal = new Decimal(0)
|
||||||
|
|
||||||
const receivedCallDate = new Date()
|
const receivedCallDate = new Date()
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
|
try {
|
||||||
await queryRunner.connect()
|
await queryRunner.connect()
|
||||||
|
|
||||||
const lastUserTransactions = await queryRunner.manager
|
const lastUserTransactions = await queryRunner.manager
|
||||||
@ -52,30 +101,13 @@ export class StatisticsResolver {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { totalGradidoCreated } = await queryRunner.manager
|
|
||||||
.createQueryBuilder()
|
|
||||||
.select('SUM(transaction.amount) AS totalGradidoCreated')
|
|
||||||
.from(DbTransaction, 'transaction')
|
|
||||||
.where('transaction.typeId = 1')
|
|
||||||
.getRawOne()
|
|
||||||
|
|
||||||
const { totalGradidoDecayed } = await queryRunner.manager
|
|
||||||
.createQueryBuilder()
|
|
||||||
.select('SUM(transaction.decay) AS totalGradidoDecayed')
|
|
||||||
.from(DbTransaction, 'transaction')
|
|
||||||
.where('transaction.decay IS NOT NULL')
|
|
||||||
.getRawOne()
|
|
||||||
|
|
||||||
await queryRunner.release()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalUsers,
|
|
||||||
activeUsers,
|
activeUsers,
|
||||||
deletedUsers,
|
|
||||||
totalGradidoCreated,
|
|
||||||
totalGradidoDecayed,
|
|
||||||
totalGradidoAvailable,
|
totalGradidoAvailable,
|
||||||
totalGradidoUnbookedDecayed,
|
totalGradidoUnbookedDecayed,
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
import { transactionLinkCode } from './TransactionLinkResolver'
|
import { transactionLinkCode } from './TransactionLinkResolver'
|
||||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||||
import { cleanDB, testEnvironment, resetToken } from '@test/helpers'
|
import { cleanDB, testEnvironment, resetToken, resetEntity } from '@test/helpers'
|
||||||
import { creationFactory } from '@/seeds/factory/creation'
|
import { creationFactory } from '@/seeds/factory/creation'
|
||||||
import { creations } from '@/seeds/creation/index'
|
import { creations } from '@/seeds/creation/index'
|
||||||
import { userFactory } from '@/seeds/factory/user'
|
import { userFactory } from '@/seeds/factory/user'
|
||||||
@ -50,7 +50,137 @@ afterAll(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('TransactionLinkResolver', () => {
|
describe('TransactionLinkResolver', () => {
|
||||||
// TODO: have this test separated into a transactionLink and a contributionLink part (if possible)
|
describe('redeemTransactionLink', () => {
|
||||||
|
describe('contributionLink', () => {
|
||||||
|
describe('input not valid', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await mutate({
|
||||||
|
mutation: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when link does not exists', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: redeemTransactionLink,
|
||||||
|
variables: {
|
||||||
|
code: 'CL-123456',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'Creation from contribution link was not successful. Error: No contribution link found to given code: CL-123456',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when link is not valid yet', async () => {
|
||||||
|
const now = new Date()
|
||||||
|
const {
|
||||||
|
data: { createContributionLink: contributionLink },
|
||||||
|
} = await mutate({
|
||||||
|
mutation: createContributionLink,
|
||||||
|
variables: {
|
||||||
|
amount: new Decimal(5),
|
||||||
|
name: 'Daily Contribution Link',
|
||||||
|
memo: 'Thank you for contribute daily to the community',
|
||||||
|
cycle: 'DAILY',
|
||||||
|
validFrom: new Date(now.getFullYear() + 1, 0, 1).toISOString(),
|
||||||
|
validTo: new Date(now.getFullYear() + 1, 11, 31, 23, 59, 59, 999).toISOString(),
|
||||||
|
maxAmountPerMonth: new Decimal(200),
|
||||||
|
maxPerCycle: 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: redeemTransactionLink,
|
||||||
|
variables: {
|
||||||
|
code: 'CL-' + contributionLink.code,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'Creation from contribution link was not successful. Error: Contribution link not valid yet',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
await resetEntity(DbContributionLink)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when contributionLink cycle is invalid', async () => {
|
||||||
|
const now = new Date()
|
||||||
|
const {
|
||||||
|
data: { createContributionLink: contributionLink },
|
||||||
|
} = await mutate({
|
||||||
|
mutation: createContributionLink,
|
||||||
|
variables: {
|
||||||
|
amount: new Decimal(5),
|
||||||
|
name: 'Daily Contribution Link',
|
||||||
|
memo: 'Thank you for contribute daily to the community',
|
||||||
|
cycle: 'INVALID',
|
||||||
|
validFrom: new Date(now.getFullYear(), 0, 1).toISOString(),
|
||||||
|
validTo: new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999).toISOString(),
|
||||||
|
maxAmountPerMonth: new Decimal(200),
|
||||||
|
maxPerCycle: 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: redeemTransactionLink,
|
||||||
|
variables: {
|
||||||
|
code: 'CL-' + contributionLink.code,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'Creation from contribution link was not successful. Error: Contribution link has unknown cycle',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
await resetEntity(DbContributionLink)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when link is no longer valid', async () => {
|
||||||
|
const now = new Date()
|
||||||
|
const {
|
||||||
|
data: { createContributionLink: contributionLink },
|
||||||
|
} = await mutate({
|
||||||
|
mutation: createContributionLink,
|
||||||
|
variables: {
|
||||||
|
amount: new Decimal(5),
|
||||||
|
name: 'Daily Contribution Link',
|
||||||
|
memo: 'Thank you for contribute daily to the community',
|
||||||
|
cycle: 'DAILY',
|
||||||
|
validFrom: new Date(now.getFullYear() - 1, 0, 1).toISOString(),
|
||||||
|
validTo: new Date(now.getFullYear() - 1, 11, 31, 23, 59, 59, 999).toISOString(),
|
||||||
|
maxAmountPerMonth: new Decimal(200),
|
||||||
|
maxPerCycle: 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: redeemTransactionLink,
|
||||||
|
variables: {
|
||||||
|
code: 'CL-' + contributionLink.code,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'Creation from contribution link was not successful. Error: Contribution link is no longer valid',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
await resetEntity(DbContributionLink)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: have this test separated into a transactionLink and a contributionLink part
|
||||||
describe('redeem daily Contribution Link', () => {
|
describe('redeem daily Contribution Link', () => {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
let contributionLink: DbContributionLink | undefined
|
let contributionLink: DbContributionLink | undefined
|
||||||
@ -237,6 +367,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('transaction links list', () => {
|
describe('transaction links list', () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
@ -305,7 +436,9 @@ describe('TransactionLinkResolver', () => {
|
|||||||
variables.userId = user.id
|
variables.userId = user.id
|
||||||
variables.pageSize = 25
|
variables.pageSize = 25
|
||||||
// bibi needs GDDs
|
// bibi needs GDDs
|
||||||
const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de')
|
const bibisCreation = creations.find(
|
||||||
|
(creation) => creation.email === 'bibi@bloxberg.de',
|
||||||
|
)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
await creationFactory(testEnv, bibisCreation!)
|
await creationFactory(testEnv, bibisCreation!)
|
||||||
// bibis transaktion links
|
// bibis transaktion links
|
||||||
@ -519,3 +652,4 @@ describe('transactionLinkCode', () => {
|
|||||||
expect(transactionLinkCode(date)).toEqual(expect.stringMatching(regexp))
|
expect(transactionLinkCode(date)).toEqual(expect.stringMatching(regexp))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|||||||
@ -170,6 +170,7 @@ export class TransactionLinkResolver {
|
|||||||
if (code.match(/^CL-/)) {
|
if (code.match(/^CL-/)) {
|
||||||
// acquire lock
|
// acquire lock
|
||||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||||
|
try {
|
||||||
logger.info('redeem contribution link...')
|
logger.info('redeem contribution link...')
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
@ -184,7 +185,7 @@ export class TransactionLinkResolver {
|
|||||||
.getOne()
|
.getOne()
|
||||||
if (!contributionLink) {
|
if (!contributionLink) {
|
||||||
logger.error('no contribution link found to given code:', code)
|
logger.error('no contribution link found to given code:', code)
|
||||||
throw new Error('No contribution link found')
|
throw new Error(`No contribution link found to given code: ${code}`)
|
||||||
}
|
}
|
||||||
logger.info('...contribution link found with id', contributionLink.id)
|
logger.info('...contribution link found with id', contributionLink.id)
|
||||||
if (new Date(contributionLink.validFrom).getTime() > now.getTime()) {
|
if (new Date(contributionLink.validFrom).getTime() > now.getTime()) {
|
||||||
@ -196,8 +197,11 @@ export class TransactionLinkResolver {
|
|||||||
}
|
}
|
||||||
if (contributionLink.validTo) {
|
if (contributionLink.validTo) {
|
||||||
if (new Date(contributionLink.validTo).setHours(23, 59, 59) < now.getTime()) {
|
if (new Date(contributionLink.validTo).setHours(23, 59, 59) < now.getTime()) {
|
||||||
logger.error('contribution link is depricated. Valid to: ', contributionLink.validTo)
|
logger.error(
|
||||||
throw new Error('Contribution link is depricated')
|
'contribution link is no longer valid. Valid to: ',
|
||||||
|
contributionLink.validTo,
|
||||||
|
)
|
||||||
|
throw new Error('Contribution link is no longer valid')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let alreadyRedeemed: DbContribution | undefined
|
let alreadyRedeemed: DbContribution | undefined
|
||||||
@ -312,6 +316,8 @@ export class TransactionLinkResolver {
|
|||||||
throw new Error(`Creation from contribution link was not successful. ${e}`)
|
throw new Error(`Creation from contribution link was not successful. ${e}`)
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
releaseLock()
|
releaseLock()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -45,6 +45,9 @@ export const executeTransaction = async (
|
|||||||
recipient: dbUser,
|
recipient: dbUser,
|
||||||
transactionLink?: dbTransactionLink | null,
|
transactionLink?: dbTransactionLink | null,
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
|
// acquire lock
|
||||||
|
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||||
|
try {
|
||||||
logger.info(
|
logger.info(
|
||||||
`executeTransaction(amount=${amount}, memo=${memo}, sender=${sender}, recipient=${recipient})...`,
|
`executeTransaction(amount=${amount}, memo=${memo}, sender=${sender}, recipient=${recipient})...`,
|
||||||
)
|
)
|
||||||
@ -64,10 +67,6 @@ export const executeTransaction = async (
|
|||||||
throw new Error(`memo text is too short (${MEMO_MIN_CHARS} characters minimum)`)
|
throw new Error(`memo text is too short (${MEMO_MIN_CHARS} characters minimum)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// acquire lock
|
|
||||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
|
||||||
|
|
||||||
try {
|
|
||||||
// validate amount
|
// validate amount
|
||||||
const receivedCallDate = new Date()
|
const receivedCallDate = new Date()
|
||||||
const sendBalance = await calculateBalance(
|
const sendBalance = await calculateBalance(
|
||||||
@ -187,10 +186,10 @@ export const executeTransaction = async (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
logger.info(`finished executeTransaction successfully`)
|
logger.info(`finished executeTransaction successfully`)
|
||||||
return true
|
|
||||||
} finally {
|
} finally {
|
||||||
releaseLock()
|
releaseLock()
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido-database",
|
"name": "gradido-database",
|
||||||
"version": "1.16.0",
|
"version": "1.17.0",
|
||||||
"description": "Gradido Database Tool to execute database migrations",
|
"description": "Gradido Database Tool to execute database migrations",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": "https://github.com/gradido/gradido/database",
|
"repository": "https://github.com/gradido/gradido/database",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bootstrap-vue-gradido-wallet",
|
"name": "bootstrap-vue-gradido-wallet",
|
||||||
"version": "1.16.0",
|
"version": "1.17.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node run/server.js",
|
"start": "node run/server.js",
|
||||||
|
|||||||
@ -23,6 +23,7 @@ describe('ContributionForm', () => {
|
|||||||
const mocks = {
|
const mocks = {
|
||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
$d: jest.fn((d) => d),
|
$d: jest.fn((d) => d),
|
||||||
|
$n: jest.fn((n) => n),
|
||||||
$store: {
|
$store: {
|
||||||
state: {
|
state: {
|
||||||
creation: ['1000', '1000', '1000'],
|
creation: ['1000', '1000', '1000'],
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
:label-no-date-selected="$t('contribution.noDateSelected')"
|
:label-no-date-selected="$t('contribution.noDateSelected')"
|
||||||
required
|
required
|
||||||
:disabled="this.form.id !== null"
|
:disabled="this.form.id !== null"
|
||||||
|
:dropleft="true"
|
||||||
>
|
>
|
||||||
<template #nav-prev-year><span></span></template>
|
<template #nav-prev-year><span></span></template>
|
||||||
<template #nav-next-year><span></span></template>
|
<template #nav-next-year><span></span></template>
|
||||||
|
|||||||
@ -9,10 +9,10 @@ describe('InputAmount', () => {
|
|||||||
|
|
||||||
const mocks = {
|
const mocks = {
|
||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
|
$n: jest.fn((n) => n),
|
||||||
$i18n: {
|
$i18n: {
|
||||||
locale: jest.fn(() => 'en'),
|
locale: jest.fn(() => 'en'),
|
||||||
},
|
},
|
||||||
$n: jest.fn((n) => String(n)),
|
|
||||||
$route: {
|
$route: {
|
||||||
params: {},
|
params: {},
|
||||||
},
|
},
|
||||||
@ -46,13 +46,14 @@ describe('InputAmount', () => {
|
|||||||
|
|
||||||
describe('amount normalization', () => {
|
describe('amount normalization', () => {
|
||||||
describe('if invalid', () => {
|
describe('if invalid', () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
|
await wrapper.setProps({ value: '12m34' })
|
||||||
valid = false
|
valid = false
|
||||||
})
|
})
|
||||||
|
|
||||||
it('is not normalized', () => {
|
it('is not normalized', () => {
|
||||||
wrapper.vm.normalizeAmount(valid)
|
wrapper.vm.normalizeAmount(false)
|
||||||
expect(wrapper.vm.amountValue).toBe(0.0)
|
expect(wrapper.vm.currentValue).toBe('12m34')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -97,13 +98,14 @@ describe('InputAmount', () => {
|
|||||||
|
|
||||||
describe('amount normalization', () => {
|
describe('amount normalization', () => {
|
||||||
describe('if invalid', () => {
|
describe('if invalid', () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
|
await wrapper.setProps({ value: '12m34' })
|
||||||
valid = false
|
valid = false
|
||||||
})
|
})
|
||||||
|
|
||||||
it('is not normalized', () => {
|
it('is not normalized', () => {
|
||||||
wrapper.vm.normalizeAmount(valid)
|
wrapper.vm.normalizeAmount(valid)
|
||||||
expect(wrapper.vm.amountValue).toBe(0.0)
|
expect(wrapper.vm.currentValue).toBe('12m34')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
trim
|
trim
|
||||||
v-focus="amountFocused"
|
v-focus="amountFocused"
|
||||||
@focus="amountFocused = true"
|
@focus="amountFocused = true"
|
||||||
@blur="normalizeAmount(true)"
|
@blur="normalizeAmount(valid)"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
></b-form-input>
|
></b-form-input>
|
||||||
@ -90,5 +90,8 @@ export default {
|
|||||||
this.currentValue = this.$n(this.amountValue, 'ungroupedDecimal')
|
this.currentValue = this.$n(this.amountValue, 'ungroupedDecimal')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.value !== '') this.normalizeAmount(true)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
type="number"
|
type="number"
|
||||||
:state="validated ? valid : false"
|
:state="validated ? valid : false"
|
||||||
step="0.5"
|
step="0.25"
|
||||||
min="0"
|
min="0"
|
||||||
:max="validMaxTime"
|
:max="validMaxTime"
|
||||||
class="bg-248"
|
class="bg-248"
|
||||||
|
|||||||
@ -125,14 +125,13 @@
|
|||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
:transactionCount="transactionCount"
|
:transactionCount="transactionCount"
|
||||||
:transactionLinkCount="transactionLinkCount"
|
:transactionLinkCount="transactionLinkCount"
|
||||||
@set-tunneled-email="setTunneledEmail"
|
|
||||||
>
|
>
|
||||||
<template #transactions>
|
<template #transactions>
|
||||||
<last-transactions
|
<last-transactions
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
:transactionCount="transactionCount"
|
:transactionCount="transactionCount"
|
||||||
:transactionLinkCount="transactionLinkCount"
|
:transactionLinkCount="transactionLinkCount"
|
||||||
v-on="$listeners"
|
@set-tunneled-email="setTunneledEmail"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #community>
|
<template #community>
|
||||||
@ -167,14 +166,13 @@
|
|||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
:transactionCount="transactionCount"
|
:transactionCount="transactionCount"
|
||||||
:transactionLinkCount="transactionLinkCount"
|
:transactionLinkCount="transactionLinkCount"
|
||||||
@set-tunneled-email="setTunneledEmail"
|
|
||||||
>
|
>
|
||||||
<template #transactions>
|
<template #transactions>
|
||||||
<last-transactions
|
<last-transactions
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
:transactionCount="transactionCount"
|
:transactionCount="transactionCount"
|
||||||
:transactionLinkCount="transactionLinkCount"
|
:transactionLinkCount="transactionLinkCount"
|
||||||
v-on="$listeners"
|
@set-tunneled-email="setTunneledEmail"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #community>
|
<template #community>
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
"noOpenContributionLinkText": "Zur Zeit gibt es keine automatischen Schöpfungen.",
|
"noOpenContributionLinkText": "Zur Zeit gibt es keine automatischen Schöpfungen.",
|
||||||
"openContributionLinks": "Öffentliche Beitrags-Linkliste",
|
"openContributionLinks": "Öffentliche Beitrags-Linkliste",
|
||||||
"openContributionLinkText": "Folgende {count} automatische Schöpfungen werden zur Zeit durch die Gemeinschaft „{name}“ bereitgestellt.",
|
"openContributionLinkText": "Folgende {count} automatische Schöpfungen werden zur Zeit durch die Gemeinschaft „{name}“ bereitgestellt.",
|
||||||
"submitContribution": "schreiben"
|
"submitContribution": "Schreiben"
|
||||||
},
|
},
|
||||||
"communityInfo": "Gemeinschaft Information",
|
"communityInfo": "Gemeinschaft Information",
|
||||||
"contact": "Kontakt",
|
"contact": "Kontakt",
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
"noOpenContributionLinkText": "Currently there are no automatic creations.",
|
"noOpenContributionLinkText": "Currently there are no automatic creations.",
|
||||||
"openContributionLinks": "Open contribution-link list",
|
"openContributionLinks": "Open contribution-link list",
|
||||||
"openContributionLinkText": "The following {count} automatic creations are currently provided by the \"{name}\" community.",
|
"openContributionLinkText": "The following {count} automatic creations are currently provided by the \"{name}\" community.",
|
||||||
"submitContribution": "writing"
|
"submitContribution": "Contribute"
|
||||||
},
|
},
|
||||||
"communityInfo": "Community Information",
|
"communityInfo": "Community Information",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
@ -126,7 +126,7 @@
|
|||||||
"firstname": "Firstname",
|
"firstname": "Firstname",
|
||||||
"from": "from",
|
"from": "from",
|
||||||
"generate_now": "Generate now",
|
"generate_now": "Generate now",
|
||||||
"hours": "Horas",
|
"hours": "Hours",
|
||||||
"lastname": "Lastname",
|
"lastname": "Lastname",
|
||||||
"memo": "Message",
|
"memo": "Message",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import Transactions from './Transactions'
|
import Transactions from './Transactions'
|
||||||
import { GdtEntryType } from '@/graphql/enums'
|
import { GdtEntryType } from '@/graphql/enums'
|
||||||
|
import { listGDTEntriesQuery } from '@/graphql/queries'
|
||||||
|
|
||||||
import { toastErrorSpy } from '@test/testSetup'
|
import { toastErrorSpy } from '@test/testSetup'
|
||||||
|
|
||||||
@ -45,8 +46,8 @@ describe('Transactions', () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = () => {
|
const Wrapper = (propsData = {}) => {
|
||||||
return mount(Transactions, { localVue, mocks })
|
return mount(Transactions, { localVue, mocks, propsData })
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('mount', () => {
|
describe('mount', () => {
|
||||||
@ -77,26 +78,14 @@ describe('Transactions', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('renders the transaction gradido transform table', () => {
|
it('renders the transaction gradido transform table when gdt is true', async () => {
|
||||||
beforeEach(() => {
|
await wrapper.setProps({
|
||||||
wrapper.setData({
|
|
||||||
gdt: true,
|
gdt: true,
|
||||||
})
|
})
|
||||||
})
|
|
||||||
expect(wrapper.findComponent({ name: 'GdtTransactionList' }).exists()).toBeTruthy()
|
expect(wrapper.findComponent({ name: 'GdtTransactionList' }).exists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe.skip('tabs', () => {
|
describe('update gdt with success', () => {
|
||||||
it('shows the GDD transactions by default', () => {
|
|
||||||
expect(wrapper.findAll('div[role="tabpanel"]').at(0).isVisible()).toBeTruthy()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does not show the GDT transactions by default', () => {
|
|
||||||
expect(wrapper.findAll('div[role="tabpanel"]').at(1).isVisible()).toBeFalsy()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('click on GDT tab', () => {
|
|
||||||
describe('server returns valid data', () => {
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
apolloMock.mockResolvedValue({
|
apolloMock.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
@ -143,81 +132,57 @@ describe('Transactions', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
wrapper.findAll('li[ role="presentation"]').at(1).find('a').trigger('click')
|
wrapper = Wrapper({ gdt: true })
|
||||||
})
|
|
||||||
|
|
||||||
it('does not show the GDD transactions', () => {
|
|
||||||
expect(wrapper.findAll('div[role="tabpanel"]').at(0).isVisible()).toBeFalsy()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('shows the GDT transactions', () => {
|
|
||||||
expect(wrapper.findAll('div[role="tabpanel"]').at(1).isVisible()).toBeTruthy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls the API', () => {
|
it('calls the API', () => {
|
||||||
expect(apolloMock).toBeCalledWith(
|
expect(apolloMock).toBeCalledWith({
|
||||||
expect.objectContaining({
|
query: listGDTEntriesQuery,
|
||||||
variables: {
|
variables: {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
)
|
})
|
||||||
|
|
||||||
|
it('does not show the GDD transactions', () => {
|
||||||
|
expect(wrapper.findAll('div.gdd-transaction-list').exists()).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the GDT transactions', () => {
|
||||||
|
expect(wrapper.findAll('div.gdt-transaction-list').exists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('scrolls to (0, 0) after API call', () => {
|
it('scrolls to (0, 0) after API call', () => {
|
||||||
expect(windowScrollToMock).toBeCalledWith(0, 0)
|
expect(windowScrollToMock).toBeCalledWith(0, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('click on GDD tab', () => {
|
describe('update current page', () => {
|
||||||
beforeEach(() => {
|
|
||||||
wrapper.findAll('li[ role="presentation"]').at(0).find('a').trigger('click')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('shows the GDD transactions', () => {
|
|
||||||
expect(wrapper.findAll('div[role="tabpanel"]').at(0).isVisible()).toBeTruthy()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does not show the GDT', () => {
|
|
||||||
expect(wrapper.findAll('div[role="tabpanel"]').at(1).isVisible()).toBeFalsy()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('server returns error', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
apolloMock.mockRejectedValue({ message: 'Ouch!' })
|
|
||||||
wrapper.findAll('li[ role="presentation"]').at(1).find('a').trigger('click')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('toasts an error message', () => {
|
|
||||||
expect(toastErrorSpy).toBeCalledWith('Ouch!')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('sets transactionGdtCount to -1', () => {
|
|
||||||
expect(wrapper.vm.transactionGdtCount).toBe(-1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe.skip('update currentPage', () => {
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
wrapper.setData({
|
wrapper.vm.currentPage = 2
|
||||||
currentPage: 2,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls the API', () => {
|
it('calls the API again', () => {
|
||||||
expect(apolloMock).toBeCalledWith(
|
expect(apolloMock).toBeCalledWith({
|
||||||
expect.objectContaining({
|
query: listGDTEntriesQuery,
|
||||||
variables: {
|
variables: {
|
||||||
currentPage: 2,
|
currentPage: 2,
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
)
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('update gdt with error', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
apolloMock.mockRejectedValue({ message: 'Oh no!' })
|
||||||
|
wrapper = Wrapper({ gdt: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts the error', () => {
|
||||||
|
expect(toastErrorSpy).toBeCalledWith('Oh no!')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
:transactionLinkCount="transactionLinkCount"
|
:transactionLinkCount="transactionLinkCount"
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
:showPagination="true"
|
:showPagination="true"
|
||||||
|
:pageSize="pageSize"
|
||||||
@update-transactions="updateTransactions"
|
@update-transactions="updateTransactions"
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
/>
|
/>
|
||||||
@ -90,6 +91,11 @@ export default {
|
|||||||
this.updateGdt()
|
this.updateGdt()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
gdt() {
|
||||||
|
if (this.gdt) {
|
||||||
|
this.updateGdt()
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido",
|
"name": "gradido",
|
||||||
"version": "1.16.0",
|
"version": "1.17.0",
|
||||||
"description": "Gradido",
|
"description": "Gradido",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": "git@github.com:gradido/gradido.git",
|
"repository": "git@github.com:gradido/gradido.git",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user