diff --git a/admin/jest.config.js b/admin/jest.config.js index 9b9842bad..3e416e7b6 100644 --- a/admin/jest.config.js +++ b/admin/jest.config.js @@ -26,5 +26,5 @@ module.exports = { testMatch: ['**/?(*.)+(spec|test).js?(x)'], // snapshotSerializers: ['jest-serializer-vue'], transformIgnorePatterns: ['/node_modules/(?!vee-validate/dist/rules)'], - testEnvironment: 'jest-environment-jsdom-sixteen', + // testEnvironment: 'jest-environment-jsdom-sixteen', // not needed anymore since jest@26, see: https://www.npmjs.com/package/jest-environment-jsdom-sixteen } diff --git a/admin/package.json b/admin/package.json index 5da80bd1f..98746be6d 100644 --- a/admin/package.json +++ b/admin/package.json @@ -70,7 +70,6 @@ "eslint-plugin-prettier": "3.3.1", "eslint-plugin-promise": "^5.1.1", "eslint-plugin-vue": "^7.20.0", - "jest-environment-jsdom-sixteen": "^2.0.0", "postcss": "^8.4.8", "postcss-html": "^1.3.0", "postcss-scss": "^4.0.3", diff --git a/admin/yarn.lock b/admin/yarn.lock index 09b543354..b5b76cee8 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -1282,17 +1282,6 @@ jest-message-util "^24.9.0" jest-mock "^24.9.0" -"@jest/fake-timers@^25.1.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.5.0.tgz#46352e00533c024c90c2bc2ad9f2959f7f114185" - integrity sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ== - dependencies: - "@jest/types" "^25.5.0" - jest-message-util "^25.5.0" - jest-mock "^25.5.0" - jest-util "^25.5.0" - lolex "^5.0.0" - "@jest/fake-timers@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" @@ -1504,16 +1493,6 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@jest/types@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" - integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" - chalk "^3.0.0" - "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -4130,14 +4109,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -8012,16 +7983,6 @@ jest-environment-jsdom-fifteen@^1.0.2: jest-util "^24.0.0" jsdom "^15.2.1" -jest-environment-jsdom-sixteen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom-sixteen/-/jest-environment-jsdom-sixteen-2.0.0.tgz#0f8c12663ccd9836d248574decffc575bfb091e1" - integrity sha512-BF+8P67aEJcd78TQzwSb9P4a73cArOWb5KgqI8eU6cHRWDIJdDRE8XTeZAmOuDSDhKpuEXjKkXwWB3GOJvqHJQ== - dependencies: - "@jest/fake-timers" "^25.1.0" - jest-mock "^25.1.0" - jest-util "^25.1.0" - jsdom "^16.2.1" - jest-environment-jsdom@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" @@ -8236,20 +8197,6 @@ jest-message-util@^24.9.0: slash "^2.0.0" stack-utils "^1.0.1" -jest-message-util@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea" - integrity sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^25.5.0" - "@types/stack-utils" "^1.0.1" - chalk "^3.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" - slash "^3.0.0" - stack-utils "^1.0.1" - jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -8272,13 +8219,6 @@ jest-mock@^24.0.0, jest-mock@^24.9.0: dependencies: "@jest/types" "^24.9.0" -jest-mock@^25.1.0, jest-mock@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.5.0.tgz#a91a54dabd14e37ecd61665d6b6e06360a55387a" - integrity sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA== - dependencies: - "@jest/types" "^25.5.0" - jest-mock@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" @@ -8555,17 +8495,6 @@ jest-util@^24.0.0, jest-util@^24.9.0: slash "^2.0.0" source-map "^0.6.0" -jest-util@^25.1.0, jest-util@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.5.0.tgz#31c63b5d6e901274d264a4fec849230aa3fa35b0" - integrity sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA== - dependencies: - "@jest/types" "^25.5.0" - chalk "^3.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - make-dir "^3.0.0" - jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" @@ -8812,7 +8741,7 @@ jsdom@^15.2.1: ws "^7.0.0" xml-name-validator "^3.0.0" -jsdom@^16.2.1, jsdom@^16.4.0: +jsdom@^16.4.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== @@ -9167,13 +9096,6 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== -lolex@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" - integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== - dependencies: - "@sinonjs/commons" "^1.7.0" - loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index f44aa584c..66bea06f5 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0044-insert_missing_contributions', + DB_VERSION: '0045-add_denied_type_and_status_to_contributions', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info diff --git a/backend/src/graphql/enum/ContributionStatus.ts b/backend/src/graphql/enum/ContributionStatus.ts new file mode 100644 index 000000000..67cdf5398 --- /dev/null +++ b/backend/src/graphql/enum/ContributionStatus.ts @@ -0,0 +1,14 @@ +import { registerEnumType } from 'type-graphql' + +export enum ContributionStatus { + PENDING = 'PENDING', + DELETED = 'DELETED', + IN_PROGRESS = 'IN_PROGRESS', + DENIED = 'DENIED', + CONFIRMED = 'CONFIRMED', +} + +registerEnumType(ContributionStatus, { + name: 'ContributionStatus', + description: 'Name of the Type of the Contribution Status', +}) diff --git a/backend/src/graphql/enum/ContributionType.ts b/backend/src/graphql/enum/ContributionType.ts new file mode 100644 index 000000000..e8529edc4 --- /dev/null +++ b/backend/src/graphql/enum/ContributionType.ts @@ -0,0 +1,12 @@ +import { registerEnumType } from 'type-graphql' + +export enum ContributionType { + ADMIN = 'ADMIN', + USER = 'USER', + LINK = 'LINK', +} + +registerEnumType(ContributionType, { + name: 'ContributionType', + description: 'Name of the Type of the Contribution', +}) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index b7ac63842..e70fe71ee 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -36,6 +36,8 @@ import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User as dbUser } from '@entity/User' import { User } from '@model/User' import { TransactionTypeId } from '@enum/TransactionTypeId' +import { ContributionType } from '@enum/ContributionType' +import { ContributionStatus } from '@enum/ContributionStatus' import Decimal from 'decimal.js-light' import { Decay } from '@model/Decay' import Paginated from '@arg/Paginated' @@ -260,6 +262,8 @@ export class AdminResolver { contribution.contributionDate = creationDateObj contribution.memo = memo contribution.moderatorId = moderator.id + contribution.contributionType = ContributionType.ADMIN + contribution.contributionStatus = ContributionStatus.PENDING logger.trace('contribution to save', contribution) await Contribution.save(contribution) @@ -337,6 +341,7 @@ export class AdminResolver { contributionToUpdate.memo = memo contributionToUpdate.contributionDate = new Date(creationDate) contributionToUpdate.moderatorId = moderator.id + contributionToUpdate.contributionStatus = ContributionStatus.PENDING await Contribution.save(contributionToUpdate) const result = new AdminUpdateContribution() @@ -387,6 +392,8 @@ export class AdminResolver { if (!contribution) { throw new Error('Contribution not found for given id.') } + contribution.contributionStatus = ContributionStatus.DELETED + await contribution.save() const res = await contribution.softRemove() return !!res } @@ -454,6 +461,7 @@ export class AdminResolver { contribution.confirmedAt = receivedCallDate contribution.confirmedBy = moderatorUser.id contribution.transactionId = transaction.id + contribution.contributionStatus = ContributionStatus.CONFIRMED await queryRunner.manager.update(Contribution, { id: contribution.id }, contribution) await queryRunner.commitTransaction() diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 3307252e4..8056ffde3 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -7,6 +7,8 @@ import { FindOperator, IsNull, getConnection } from '@dbTools/typeorm' import ContributionArgs from '@arg/ContributionArgs' import Paginated from '@arg/Paginated' import { Order } from '@enum/Order' +import { ContributionType } from '@enum/ContributionType' +import { ContributionStatus } from '@enum/ContributionStatus' import { Contribution, ContributionListResult } from '@model/Contribution' import { UnconfirmedContribution } from '@model/UnconfirmedContribution' import { User } from '@model/User' @@ -43,6 +45,8 @@ export class ContributionResolver { contribution.createdAt = new Date() contribution.contributionDate = creationDateObj contribution.memo = memo + contribution.contributionType = ContributionType.USER + contribution.contributionStatus = ContributionStatus.PENDING logger.trace('contribution to save', contribution) await dbContribution.save(contribution) @@ -66,6 +70,8 @@ export class ContributionResolver { if (contribution.confirmedAt) { throw new Error('A confirmed contribution can not be deleted') } + contribution.contributionStatus = ContributionStatus.DELETED + await contribution.save() const res = await contribution.softRemove() return !!res } @@ -164,6 +170,7 @@ export class ContributionResolver { contributionToUpdate.amount = amount contributionToUpdate.memo = memo contributionToUpdate.contributionDate = new Date(creationDate) + contributionToUpdate.contributionStatus = ContributionStatus.PENDING dbContribution.save(contributionToUpdate) return new UnconfirmedContribution(contributionToUpdate, user, creations) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 8696065ed..ccc0f628d 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -26,6 +26,8 @@ import { User } from '@model/User' import { calculateDecay } from '@/util/decay' import { executeTransaction } from './TransactionResolver' import { Order } from '@enum/Order' +import { ContributionType } from '@enum/ContributionType' +import { ContributionStatus } from '@enum/ContributionStatus' import { Contribution as DbContribution } from '@entity/Contribution' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { getUserCreation, validateContribution } from './util/creations' @@ -231,6 +233,9 @@ export class TransactionLinkResolver { contribution.memo = contributionLink.memo contribution.amount = contributionLink.amount contribution.contributionLinkId = contributionLink.id + contribution.contributionType = ContributionType.LINK + contribution.contributionStatus = ContributionStatus.CONFIRMED + await queryRunner.manager.insert(DbContribution, contribution) const lastTransaction = await queryRunner.manager diff --git a/database/entity/0045-add_denied_type_and_status_to_contributions/Contribution.ts b/database/entity/0045-add_denied_type_and_status_to_contributions/Contribution.ts new file mode 100644 index 000000000..c376ae53e --- /dev/null +++ b/database/entity/0045-add_denied_type_and_status_to_contributions/Contribution.ts @@ -0,0 +1,83 @@ +import Decimal from 'decimal.js-light' +import { + BaseEntity, + Column, + Entity, + PrimaryGeneratedColumn, + DeleteDateColumn, + JoinColumn, + ManyToOne, +} from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' +import { User } from '../User' + +@Entity('contributions') +export class Contribution extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ unsigned: true, nullable: false, name: 'user_id' }) + userId: number + + @ManyToOne(() => User, (user) => user.contributions) + @JoinColumn({ name: 'user_id' }) + user: User + + @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' }) + createdAt: Date + + @Column({ type: 'datetime', nullable: false, name: 'contribution_date' }) + contributionDate: Date + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + amount: Decimal + + @Column({ unsigned: true, nullable: true, name: 'moderator_id' }) + moderatorId: number + + @Column({ unsigned: true, nullable: true, name: 'contribution_link_id' }) + contributionLinkId: number + + @Column({ unsigned: true, nullable: true, name: 'confirmed_by' }) + confirmedBy: number + + @Column({ nullable: true, name: 'confirmed_at' }) + confirmedAt: Date + + @Column({ unsigned: true, nullable: true, name: 'denied_by' }) + deniedBy: number + + @Column({ nullable: true, name: 'denied_at' }) + deniedAt: Date + + @Column({ + name: 'contribution_type', + length: 12, + nullable: false, + collation: 'utf8mb4_unicode_ci', + }) + contributionType: string + + @Column({ + name: 'contribution_status', + length: 12, + nullable: false, + collation: 'utf8mb4_unicode_ci', + }) + contributionStatus: string + + @Column({ unsigned: true, nullable: true, name: 'transaction_id' }) + transactionId: number + + @DeleteDateColumn({ name: 'deleted_at' }) + deletedAt: Date | null +} diff --git a/database/entity/Contribution.ts b/database/entity/Contribution.ts index 82dd6478c..800e7f9cd 100644 --- a/database/entity/Contribution.ts +++ b/database/entity/Contribution.ts @@ -1 +1 @@ -export { Contribution } from './0039-contributions_table/Contribution' +export { Contribution } from './0045-add_denied_type_and_status_to_contributions/Contribution' diff --git a/database/migrations/0045-add_denied_type_and_status_to_contributions.ts b/database/migrations/0045-add_denied_type_and_status_to_contributions.ts new file mode 100644 index 000000000..b3653589b --- /dev/null +++ b/database/migrations/0045-add_denied_type_and_status_to_contributions.ts @@ -0,0 +1,39 @@ +/* MIGRATION TO ADD denied_by, denied_at, contribution_type and contrinution_status +FIELDS TO contributions */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE `contributions` ADD COLUMN `denied_at` datetime DEFAULT NULL AFTER `confirmed_at`;', + ) + await queryFn( + 'ALTER TABLE `contributions` ADD COLUMN `denied_by` int(10) unsigned DEFAULT NULL AFTER `denied_at`;', + ) + await queryFn( + 'ALTER TABLE `contributions` ADD COLUMN `contribution_type` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT "ADMIN" AFTER `denied_by`;', + ) + await queryFn( + 'ALTER TABLE `contributions` ADD COLUMN `contribution_status` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT "PENDING" AFTER `contribution_type`;', + ) + await queryFn( + 'UPDATE `contributions` SET `contribution_type` = "LINK" WHERE `contribution_link_id` IS NOT NULL;', + ) + await queryFn( + 'UPDATE `contributions` SET `contribution_type` = "USER" WHERE `contribution_link_id` IS NULL AND `moderator_id` IS NULL;', + ) + await queryFn( + 'UPDATE `contributions` SET `contribution_status` = "CONFIRMED" WHERE `confirmed_at` IS NOT NULL;', + ) + await queryFn( + 'UPDATE `contributions` SET `contribution_status` = "DELETED" WHERE `deleted_at` IS NOT NULL;', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `contributions` DROP COLUMN `contribution_status`;') + await queryFn('ALTER TABLE `contributions` DROP COLUMN `contribution_type`;') + await queryFn('ALTER TABLE `contributions` DROP COLUMN `denied_by`;') + await queryFn('ALTER TABLE `contributions` DROP COLUMN `denied_at`;') +} diff --git a/frontend/jest.config.js b/frontend/jest.config.js index 2a52ec707..a32330f3b 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -23,5 +23,5 @@ module.exports = { testMatch: ['**/?(*.)+(spec|test).js?(x)'], // snapshotSerializers: ['jest-serializer-vue'], transformIgnorePatterns: ['/node_modules/(?!vee-validate/dist/rules)'], - testEnvironment: 'jest-environment-jsdom-sixteen', + // testEnvironment: 'jest-environment-jsdom-sixteen', // not needed anymore since jest@26, see: https://www.npmjs.com/package/jest-environment-jsdom-sixteen } diff --git a/frontend/package.json b/frontend/package.json index 71baf4764..8121e2ca2 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,7 +44,6 @@ "identity-obj-proxy": "^3.0.0", "jest": "^26.6.3", "jest-canvas-mock": "^2.3.1", - "jest-environment-jsdom-sixteen": "^2.0.0", "jwt-decode": "^3.1.2", "portal-vue": "^2.1.7", "prettier": "^2.2.1", diff --git a/frontend/src/components/Contributions/ContributionForm.spec.js b/frontend/src/components/Contributions/ContributionForm.spec.js index eb1509328..cf47577a3 100644 --- a/frontend/src/components/Contributions/ContributionForm.spec.js +++ b/frontend/src/components/Contributions/ContributionForm.spec.js @@ -46,46 +46,274 @@ describe('ContributionForm', () => { }) describe('empty form data', () => { - describe('buttons', () => { - it('has reset enabled', () => { - expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy() + describe('button', () => { + it('has submit disabled', () => { + expect(wrapper.find('button[data-test="button-submit"]').attributes('disabled')).toBe( + 'disabled', + ) + }) + }) + }) + + describe('dates', () => { + beforeEach(async () => { + await wrapper.setData({ + form: { + id: null, + date: '', + memo: '', + amount: '', + }, + }) + }) + + describe('actual date', () => { + describe('same month', () => { + beforeEach(async () => { + const now = new Date().toISOString() + await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now) + }) + + describe('isThisMonth', () => { + it('has true', () => { + expect(wrapper.vm.isThisMonth).toBe(true) + }) + }) }) - it('has submit disabled', () => { - expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled') + describe('month before', () => { + beforeEach(async () => { + await wrapper + .findComponent({ name: 'BFormDatepicker' }) + .vm.$emit('input', wrapper.vm.minimalDate) + }) + + describe('isThisMonth', () => { + it('has false', () => { + expect(wrapper.vm.isThisMonth).toBe(false) + }) + }) + }) + }) + + describe('date in middle of year', () => { + describe('same month', () => { + beforeEach(async () => { + // jest.useFakeTimers('modern') + // jest.setSystemTime(new Date('2020-07-06')) + // await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now) + await wrapper.setData({ + maximalDate: new Date(2020, 6, 6), + form: { date: new Date(2020, 6, 6) }, + }) + }) + + describe('minimalDate', () => { + it('has "2020-06-01T00:00:00.000Z"', () => { + expect(wrapper.vm.minimalDate.toISOString()).toBe('2020-06-01T00:00:00.000Z') + }) + }) + + describe('isThisMonth', () => { + it('has true', () => { + expect(wrapper.vm.isThisMonth).toBe(true) + }) + }) + }) + + describe('month before', () => { + beforeEach(async () => { + // jest.useFakeTimers('modern') + // jest.setSystemTime(new Date('2020-07-06')) + // console.log('middle of year date – now:', wrapper.vm.minimalDate) + // await wrapper + // .findComponent({ name: 'BFormDatepicker' }) + // .vm.$emit('input', wrapper.vm.minimalDate) + await wrapper.setData({ + maximalDate: new Date(2020, 6, 6), + form: { date: new Date(2020, 5, 6) }, + }) + }) + + describe('minimalDate', () => { + it('has "2020-06-01T00:00:00.000Z"', () => { + expect(wrapper.vm.minimalDate.toISOString()).toBe('2020-06-01T00:00:00.000Z') + }) + }) + + describe('isThisMonth', () => { + it('has false', () => { + expect(wrapper.vm.isThisMonth).toBe(false) + }) + }) + }) + }) + + describe('date in january', () => { + describe('same month', () => { + beforeEach(async () => { + await wrapper.setData({ + maximalDate: new Date(2020, 0, 6), + form: { date: new Date(2020, 0, 6) }, + }) + }) + + describe('minimalDate', () => { + it('has "2019-12-01T00:00:00.000Z"', () => { + expect(wrapper.vm.minimalDate.toISOString()).toBe('2019-12-01T00:00:00.000Z') + }) + }) + + describe('isThisMonth', () => { + it('has true', () => { + expect(wrapper.vm.isThisMonth).toBe(true) + }) + }) + }) + + describe('month before', () => { + beforeEach(async () => { + // jest.useFakeTimers('modern') + // jest.setSystemTime(new Date('2020-07-06')) + // console.log('middle of year date – now:', wrapper.vm.minimalDate) + // await wrapper + // .findComponent({ name: 'BFormDatepicker' }) + // .vm.$emit('input', wrapper.vm.minimalDate) + await wrapper.setData({ + maximalDate: new Date(2020, 0, 6), + form: { date: new Date(2019, 11, 6) }, + }) + }) + + describe('minimalDate', () => { + it('has "2019-12-01T00:00:00.000Z"', () => { + expect(wrapper.vm.minimalDate.toISOString()).toBe('2019-12-01T00:00:00.000Z') + }) + }) + + describe('isThisMonth', () => { + it('has false', () => { + expect(wrapper.vm.isThisMonth).toBe(false) + }) + }) }) }) }) describe('set contrubtion', () => { - describe('fill in form data', () => { + describe('fill in form data with "id === null"', () => { const now = new Date().toISOString() beforeEach(async () => { await wrapper.setData({ form: { id: null, - date: now, - memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...', - amount: '200', + date: '', + memo: '', + amount: '', }, }) }) - describe('buttons', () => { - it('has reset enabled', () => { - expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy() + describe('invalid form data', () => { + beforeEach(async () => { + await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now) + await wrapper.find('#contribution-amount').find('input').setValue('200') }) - it('has submit enabled', () => { - expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeFalsy() + describe('memo lenght < 5, others are valid', () => { + beforeEach(async () => { + await wrapper.find('#contribution-memo').find('textarea').setValue('1234') + }) + + describe('button', () => { + describe('submit', () => { + it('has disabled', () => { + expect( + wrapper.find('button[data-test="button-submit"]').attributes('disabled'), + ).toBe('disabled') + }) + }) + }) + }) + + describe('memo lenght > 255, others are valid', () => { + beforeEach(async () => { + await wrapper + .find('#contribution-memo') + .find('textarea') + .setValue( + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' + + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' + + '01234567890123456789012345678901234567890123456789012345', + ) + await wrapper.vm.$nextTick() + }) + + describe('button', () => { + describe('submit', () => { + it('has disabled', () => { + expect( + wrapper.find('button[data-test="button-submit"]').attributes('disabled'), + ).toBe('disabled') + }) + }) + }) + }) + }) + + describe('valid form data', () => { + beforeEach(async () => { + await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now) + await wrapper + .find('#contribution-memo') + .find('textarea') + .setValue('Mein Beitrag zur Gemeinschaft für diesen Monat ...') + await wrapper.find('#contribution-amount').find('input').setValue('200') + }) + + describe('button', () => { + describe('submit', () => { + it('has enabled', () => { + expect( + wrapper.find('button[data-test="button-submit"]').attributes('disabled'), + ).toBeFalsy() + }) + + it('has label "contribution.submit"', () => { + expect(wrapper.find('button[data-test="button-submit"]').text()).toContain( + 'contribution.submit', + ) + }) + }) + }) + + describe('on trigger submit', () => { + beforeEach(async () => { + await wrapper.find('form').trigger('submit') + }) + + it('emits "set-contribution"', () => { + expect(wrapper.emitted('set-contribution')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + id: null, + date: now, + memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...', + amount: '200', + }, + ]), + ]), + ) + }) }) }) }) }) describe('update contrubtion', () => { - describe('fill in form data and "id"', () => { + describe('fill in form data with set "id"', () => { const now = new Date().toISOString() beforeEach(async () => { @@ -93,19 +321,104 @@ describe('ContributionForm', () => { form: { id: 2, date: now, - memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...', - amount: '200', + memo: 'Mein kommerzieller Beitrag für diesen Monat ...', + amount: '100', }, }) }) - describe('buttons', () => { - it('has reset enabled', () => { - expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy() + describe('invalid form data', () => { + beforeEach(async () => { + await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now) + await wrapper.find('#contribution-amount').find('input').setValue('200') }) - it('has submit enabled', () => { - expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeFalsy() + describe('memo lenght < 5, others are valid', () => { + beforeEach(async () => { + await wrapper.find('#contribution-memo').find('textarea').setValue('1234') + }) + + describe('button', () => { + describe('submit', () => { + it('has disabled', () => { + expect( + wrapper.find('button[data-test="button-submit"]').attributes('disabled'), + ).toBe('disabled') + }) + }) + }) + }) + + describe('memo lenght > 255, others are valid', () => { + beforeEach(async () => { + await wrapper + .find('#contribution-memo') + .find('textarea') + .setValue( + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' + + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' + + '01234567890123456789012345678901234567890123456789012345', + ) + await wrapper.vm.$nextTick() + }) + + describe('button', () => { + describe('submit', () => { + it('has disabled', () => { + expect( + wrapper.find('button[data-test="button-submit"]').attributes('disabled'), + ).toBe('disabled') + }) + }) + }) + }) + }) + + describe('valid form data', () => { + beforeEach(async () => { + await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now) + await wrapper + .find('#contribution-memo') + .find('textarea') + .setValue('Mein Beitrag zur Gemeinschaft für diesen Monat ...') + await wrapper.find('#contribution-amount').find('input').setValue('200') + }) + + describe('button', () => { + describe('submit', () => { + it('has enabled', () => { + expect( + wrapper.find('button[data-test="button-submit"]').attributes('disabled'), + ).toBeFalsy() + }) + + it('has label "form.change"', () => { + expect(wrapper.find('button[data-test="button-submit"]').text()).toContain( + 'form.change', + ) + }) + }) + }) + + describe('on trigger submit', () => { + beforeEach(async () => { + await wrapper.find('form').trigger('submit') + }) + + it('emits "update-contribution"', () => { + expect(wrapper.emitted('update-contribution')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + id: 2, + date: now, + memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...', + amount: '200', + }, + ]), + ]), + ) + }) }) }) }) diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue index 5fad059fb..3a9010ec2 100644 --- a/frontend/src/components/Contributions/ContributionForm.vue +++ b/frontend/src/components/Contributions/ContributionForm.vue @@ -4,8 +4,8 @@

{{ $t('contribution.formText.yourContribution') }}

{{ $t('contribution.formText.bringYourTalentsTo') }}
    -
  • -
  • +
  • +
@@ -73,12 +73,12 @@
- + {{ $t('form.cancel') }} - + {{ form.id ? $t('form.change') : $t('contribution.submit') }} @@ -108,11 +108,8 @@ export default { }, submit() { this.form.amount = this.numberFormat(this.form.amount) - if (this.form.id) { - this.$emit('update-contribution', this.form) - } else { - this.$emit('set-contribution', this.form) - } + // spreading is needed for testing + this.$emit(this.form.id ? 'update-contribution' : 'set-contribution', { ...this.form }) this.reset() }, reset() { @@ -122,53 +119,47 @@ export default { this.form.memo = '' this.form.amount = '' }, + textForMonth(date, availableAmount) { + const obj = { + monthAndYear: this.$d(date, 'monthAndYear'), + creation: availableAmount, + } + return this.$t('contribution.formText.openAmountForMonth', obj) + }, }, computed: { - /* - * minimalDate() = Sets the date to the 1st of the previous month. - * - */ minimalDate() { - return new Date(this.maximalDate.getFullYear(), this.maximalDate.getMonth() - 1, 1) + // sets the date to the 1st of the previous month + let date = new Date(this.maximalDate) // has to be a new object, because of 'setMonth' changes the objects date + date = new Date(date.setMonth(date.getMonth() - 1)) + return new Date(date.getFullYear(), date.getMonth(), 1) }, disabled() { - if ( + return ( this.form.date === '' || this.form.memo.length < this.minlength || + this.form.memo.length > this.maxlength || parseInt(this.form.amount) <= 0 || parseInt(this.form.amount) > 1000 || (this.isThisMonth && parseInt(this.form.amount) > parseInt(this.maxGddThisMonth)) || (!this.isThisMonth && parseInt(this.form.amount) > parseInt(this.maxGddLastMonth)) ) - return true - return false - }, - lastMonthObject() { - // new Date().getMonth === 1 If the current month is January, then one year must be gone back in the previous month - const obj = { - monthAndYear: this.$d(new Date(this.minimalDate), 'monthAndYear'), - creation: this.maxGddLastMonth, - } - return this.$t('contribution.formText.openAmountForMonth', obj) - }, - thisMonthObject() { - const obj = { - monthAndYear: this.$d(new Date(), 'monthAndYear'), - creation: this.maxGddThisMonth, - } - return this.$t('contribution.formText.openAmountForMonth', obj) }, isThisMonth() { - return new Date(this.form.date).getMonth() === new Date().getMonth() + const formDate = new Date(this.form.date) + return ( + formDate.getFullYear() === this.maximalDate.getFullYear() && + formDate.getMonth() === this.maximalDate.getMonth() + ) }, maxGddLastMonth() { - // When edited, the amount is added back on top of the amount + // when existing contribution is edited, the amount is added back on top of the amount return this.form.id && !this.isThisMonth ? parseInt(this.$store.state.creation[1]) + parseInt(this.updateAmount) : this.$store.state.creation[1] }, maxGddThisMonth() { - // When edited, the amount is added back on top of the amount + // when existing contribution is edited, the amount is added back on top of the amount return this.form.id && this.isThisMonth ? parseInt(this.$store.state.creation[2]) + parseInt(this.updateAmount) : this.$store.state.creation[2] diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 829b63dcf..fe7ab7947 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2156,17 +2156,6 @@ jest-message-util "^24.9.0" jest-mock "^24.9.0" -"@jest/fake-timers@^25.1.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.5.0.tgz#46352e00533c024c90c2bc2ad9f2959f7f114185" - integrity sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ== - dependencies: - "@jest/types" "^25.5.0" - jest-message-util "^25.5.0" - jest-mock "^25.5.0" - jest-util "^25.5.0" - lolex "^5.0.0" - "@jest/fake-timers@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" @@ -2378,16 +2367,6 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@jest/types@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" - integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" - chalk "^3.0.0" - "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -2482,11 +2461,6 @@ string-width "^2.0.0" strip-ansi "^5" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.7": version "7.1.17" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.17.tgz#f50ac9d20d64153b510578d84f9643f9a3afbe64" @@ -3293,11 +3267,6 @@ acorn@^8.0.5: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== -acorn@^8.2.4: - version "8.6.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" - integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== - acorn@^8.5.0, acorn@^8.7.0: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" @@ -3308,13 +3277,6 @@ address@^1.0.3: resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -4657,14 +4619,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -4995,7 +4949,7 @@ colors@^1.1.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -5578,13 +5532,6 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== - dependencies: - ms "2.1.2" - debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -5599,6 +5546,13 @@ debug@^4.0.1, debug@^4.1.1: dependencies: ms "2.1.2" +debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" @@ -7162,15 +7116,6 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -7864,15 +7809,6 @@ http-parser-js@>=0.5.1: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" @@ -7906,14 +7842,6 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -8430,11 +8358,6 @@ is-potential-custom-element-name@^1.0.0: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - is-regex@^1.0.4, is-regex@^1.1.1, is-regex@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" @@ -8849,16 +8772,6 @@ jest-environment-jsdom-fifteen@^1.0.2: jest-util "^24.0.0" jsdom "^15.2.1" -jest-environment-jsdom-sixteen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom-sixteen/-/jest-environment-jsdom-sixteen-2.0.0.tgz#0f8c12663ccd9836d248574decffc575bfb091e1" - integrity sha512-BF+8P67aEJcd78TQzwSb9P4a73cArOWb5KgqI8eU6cHRWDIJdDRE8XTeZAmOuDSDhKpuEXjKkXwWB3GOJvqHJQ== - dependencies: - "@jest/fake-timers" "^25.1.0" - jest-mock "^25.1.0" - jest-util "^25.1.0" - jsdom "^16.2.1" - jest-environment-jsdom@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" @@ -9073,20 +8986,6 @@ jest-message-util@^24.9.0: slash "^2.0.0" stack-utils "^1.0.1" -jest-message-util@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea" - integrity sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^25.5.0" - "@types/stack-utils" "^1.0.1" - chalk "^3.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" - slash "^3.0.0" - stack-utils "^1.0.1" - jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -9109,13 +9008,6 @@ jest-mock@^24.0.0, jest-mock@^24.9.0: dependencies: "@jest/types" "^24.9.0" -jest-mock@^25.1.0, jest-mock@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.5.0.tgz#a91a54dabd14e37ecd61665d6b6e06360a55387a" - integrity sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA== - dependencies: - "@jest/types" "^25.5.0" - jest-mock@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" @@ -9392,17 +9284,6 @@ jest-util@^24.0.0, jest-util@^24.9.0: slash "^2.0.0" source-map "^0.6.0" -jest-util@^25.1.0, jest-util@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.5.0.tgz#31c63b5d6e901274d264a4fec849230aa3fa35b0" - integrity sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA== - dependencies: - "@jest/types" "^25.5.0" - chalk "^3.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - make-dir "^3.0.0" - jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" @@ -9655,39 +9536,6 @@ jsdom@^15.2.1: ws "^7.0.0" xml-name-validator "^3.0.0" -jsdom@^16.2.1: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsdom@^16.4.0: version "16.5.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.1.tgz#4ced6bbd7b77d67fb980e64d9e3e6fb900f97dd6" @@ -10050,7 +9898,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.7.0: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -10067,13 +9915,6 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== -lolex@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" - integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== - dependencies: - "@sinonjs/commons" "^1.7.0" - loose-envify@^1.0.0, loose-envify@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -13848,13 +13689,6 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -14792,15 +14626,6 @@ whatwg-url@^8.0.0: tr46 "^2.0.2" webidl-conversions "^6.1.0" -whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.1, which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -14923,7 +14748,7 @@ ws@^6.0.0, ws@^6.2.1: dependencies: async-limiter "~1.0.0" -ws@^7.0.0, ws@^7.4.6: +ws@^7.0.0: version "7.5.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==