diff --git a/admin/jest.config.js b/admin/jest.config.js index b7226bd8f..9b9842bad 100644 --- a/admin/jest.config.js +++ b/admin/jest.config.js @@ -22,7 +22,7 @@ module.exports = { '^.+\\.(js|jsx)?$': 'babel-jest', '/node_modules/vee-validate/dist/rules': 'babel-jest', }, - setupFiles: ['/test/testSetup.js'], + setupFiles: ['/test/testSetup.js', 'jest-canvas-mock'], testMatch: ['**/?(*.)+(spec|test).js?(x)'], // snapshotSerializers: ['jest-serializer-vue'], transformIgnorePatterns: ['/node_modules/(?!vee-validate/dist/rules)'], diff --git a/admin/package.json b/admin/package.json index e36308fd9..cbbce5a2a 100644 --- a/admin/package.json +++ b/admin/package.json @@ -38,7 +38,9 @@ "graphql": "^15.6.1", "identity-obj-proxy": "^3.0.0", "jest": "26.6.3", + "jest-canvas-mock": "^2.3.1", "portal-vue": "^2.1.7", + "qrcanvas-vue": "2.1.1", "regenerator-runtime": "^0.13.9", "stats-webpack-plugin": "^0.7.0", "vue": "^2.6.11", diff --git a/admin/public/img/gdd-coin.png b/admin/public/img/gdd-coin.png new file mode 100644 index 000000000..32cb8b2b2 Binary files /dev/null and b/admin/public/img/gdd-coin.png differ diff --git a/admin/src/components/ContributionLink.spec.js b/admin/src/components/ContributionLink.spec.js new file mode 100644 index 000000000..f1b9cfb97 --- /dev/null +++ b/admin/src/components/ContributionLink.spec.js @@ -0,0 +1,49 @@ +import { mount } from '@vue/test-utils' +import ContributionLink from './ContributionLink.vue' + +const localVue = global.localVue + +const mocks = { + $t: jest.fn((t) => t), +} + +const propsData = { + items: [ + { + id: 1, + name: 'Meditation', + memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l', + amount: '200', + validFrom: '2022-04-01', + validTo: '2022-08-01', + cycle: 'täglich', + maxPerCycle: '3', + maxAmountPerMonth: 0, + link: 'https://localhost/redeem/CL-1a2345678', + }, + ], + count: 1, +} + +describe('ContributionLink', () => { + let wrapper + + const Wrapper = () => { + return mount(ContributionLink, { localVue, mocks, propsData }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the Div Element ".contribution-link"', () => { + expect(wrapper.find('div.contribution-link').exists()).toBe(true) + }) + + it('emits toggle::collapse new Contribution', async () => { + wrapper.vm.editContributionLinkData() + expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy() + }) + }) +}) diff --git a/admin/src/components/ContributionLink.vue b/admin/src/components/ContributionLink.vue new file mode 100644 index 000000000..893e202f4 --- /dev/null +++ b/admin/src/components/ContributionLink.vue @@ -0,0 +1,66 @@ + + diff --git a/admin/src/components/ContributionLinkForm.spec.js b/admin/src/components/ContributionLinkForm.spec.js new file mode 100644 index 000000000..9c7c33c52 --- /dev/null +++ b/admin/src/components/ContributionLinkForm.spec.js @@ -0,0 +1,102 @@ +import { mount } from '@vue/test-utils' +import ContributionLinkForm from './ContributionLinkForm.vue' + +const localVue = global.localVue + +global.alert = jest.fn() + +const propsData = { + contributionLinkData: {}, +} + +const mocks = { + $t: jest.fn((t) => t), +} + +// const mockAPIcall = jest.fn() + +describe('ContributionLinkForm', () => { + let wrapper + + const Wrapper = () => { + return mount(ContributionLinkForm, { localVue, mocks, propsData }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the Div Element ".contribution-link-form"', () => { + expect(wrapper.find('div.contribution-link-form').exists()).toBe(true) + }) + + describe('call onReset', () => { + it('form has the set data', () => { + beforeEach(() => { + wrapper.setData({ + form: { + name: 'name', + memo: 'memo', + amount: 100, + validFrom: 'validFrom', + validTo: 'validTo', + cycle: 'ONCE', + maxPerCycle: 1, + maxAmountPerMonth: 100, + }, + }) + wrapper.vm.onReset() + }) + expect(wrapper.vm.form).toEqual({ + amount: null, + cycle: 'ONCE', + validTo: null, + maxAmountPerMonth: '0', + memo: null, + name: null, + maxPerCycle: 1, + validFrom: null, + }) + }) + }) + + describe('call onSubmit', () => { + it('response with the contribution link url', () => { + wrapper.vm.onSubmit() + }) + }) + + // describe('successfull submit', () => { + // beforeEach(async () => { + // mockAPIcall.mockResolvedValue({ + // data: { + // createContributionLink: { + // link: 'https://localhost/redeem/CL-1a2345678', + // }, + // }, + // }) + // await wrapper.find('input.test-validFrom').setValue('2022-6-18') + // await wrapper.find('input.test-validTo').setValue('2022-7-18') + // await wrapper.find('input.test-name').setValue('test name') + // await wrapper.find('input.test-memo').setValue('test memo') + // await wrapper.find('input.test-amount').setValue('100') + // await wrapper.find('form').trigger('submit') + // }) + + // it('calls the API', () => { + // expect(mockAPIcall).toHaveBeenCalledWith( + // expect.objectContaining({ + // variables: { + // link: 'https://localhost/redeem/CL-1a2345678', + // }, + // }), + // ) + // }) + + // it('displays the new username', () => { + // expect(wrapper.find('div.display-username').text()).toEqual('@username') + // }) + // }) + }) +}) diff --git a/admin/src/components/ContributionLinkForm.vue b/admin/src/components/ContributionLinkForm.vue new file mode 100644 index 000000000..6fb9ee594 --- /dev/null +++ b/admin/src/components/ContributionLinkForm.vue @@ -0,0 +1,217 @@ + + diff --git a/admin/src/components/ContributionLinkList.spec.js b/admin/src/components/ContributionLinkList.spec.js new file mode 100644 index 000000000..0b9d131bd --- /dev/null +++ b/admin/src/components/ContributionLinkList.spec.js @@ -0,0 +1,147 @@ +import { mount } from '@vue/test-utils' +import ContributionLinkList from './ContributionLinkList.vue' +import { toastSuccessSpy, toastErrorSpy } from '../../test/testSetup' +// import { deleteContributionLink } from '../graphql/deleteContributionLink' + +const localVue = global.localVue + +const mockAPIcall = jest.fn() + +const mocks = { + $t: jest.fn((t) => t), + $apollo: { + mutate: mockAPIcall, + }, +} + +const propsData = { + items: [ + { + id: 1, + name: 'Meditation', + memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l', + amount: '200', + validFrom: '2022-04-01', + validTo: '2022-08-01', + cycle: 'täglich', + maxPerCycle: '3', + maxAmountPerMonth: 0, + link: 'https://localhost/redeem/CL-1a2345678', + }, + ], +} + +describe('ContributionLinkList', () => { + let wrapper + + const Wrapper = () => { + return mount(ContributionLinkList, { localVue, mocks, propsData }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the Div Element ".contribution-link-list"', () => { + expect(wrapper.find('div.contribution-link-list').exists()).toBe(true) + }) + + it('renders table with contribution link', () => { + expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain( + 'Meditation', + ) + }) + + describe('edit contribution link', () => { + beforeEach(() => { + wrapper.vm.editContributionLink() + }) + + it('emits editContributionLinkData', async () => { + expect(wrapper.vm.$emit('editContributionLinkData')).toBeTruthy() + }) + }) + + describe('delete contribution link', () => { + let spy + + beforeEach(async () => { + jest.clearAllMocks() + wrapper.vm.deleteContributionLink() + }) + + describe('with success', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + spy.mockImplementation(() => Promise.resolve('some value')) + mockAPIcall.mockResolvedValue() + await wrapper.find('.test-delete-link').trigger('click') + }) + + it('opens the modal ', () => { + expect(spy).toBeCalled() + }) + + it.skip('calls the API', () => { + // expect(mockAPIcall).toBeCalledWith( + // expect.objectContaining({ + // mutation: deleteContributionLink, + // variables: { + // id: 1, + // }, + // }), + // ) + }) + + it('toasts a success message', () => { + expect(toastSuccessSpy).toBeCalledWith('TODO: request message deleted ') + }) + }) + + describe('with error', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + spy.mockImplementation(() => Promise.resolve('some value')) + mockAPIcall.mockRejectedValue({ message: 'Something went wrong :(' }) + await wrapper.find('.test-delete-link').trigger('click') + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Something went wrong :(') + }) + }) + + describe('cancel delete', () => { + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + spy.mockImplementation(() => Promise.resolve(false)) + mockAPIcall.mockResolvedValue() + await wrapper.find('.test-delete-link').trigger('click') + }) + + it('does not call the API', () => { + expect(mockAPIcall).not.toBeCalled() + }) + }) + }) + + describe('onClick showButton', () => { + it('modelData contains contribution link', () => { + wrapper.find('button.test-show').trigger('click') + expect(wrapper.vm.modalData).toEqual({ + amount: '200', + cycle: 'täglich', + id: 1, + link: 'https://localhost/redeem/CL-1a2345678', + maxAmountPerMonth: 0, + maxPerCycle: '3', + memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l', + name: 'Meditation', + validFrom: '2022-04-01', + validTo: '2022-08-01', + }) + }) + }) + }) +}) diff --git a/admin/src/components/ContributionLinkList.vue b/admin/src/components/ContributionLinkList.vue new file mode 100644 index 000000000..518d7d57e --- /dev/null +++ b/admin/src/components/ContributionLinkList.vue @@ -0,0 +1,106 @@ + + diff --git a/admin/src/components/FigureQrCode.spec.js b/admin/src/components/FigureQrCode.spec.js new file mode 100644 index 000000000..2c18a31a1 --- /dev/null +++ b/admin/src/components/FigureQrCode.spec.js @@ -0,0 +1,30 @@ +import { mount } from '@vue/test-utils' +import FigureQrCode from './FigureQrCode.vue' + +const localVue = global.localVue + +const propsData = { + link: '', +} + +describe('FigureQrCode', () => { + let wrapper + + const Wrapper = () => { + return mount(FigureQrCode, { localVue, propsData }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the Div Element ".figure-qr-code"', () => { + expect(wrapper.find('div.figure-qr-code').exists()).toBe(true) + }) + + it('renders the QRCanvas Element ".canvas"', () => { + expect(wrapper.find('.canvas').exists()).toBe(true) + }) + }) +}) diff --git a/admin/src/components/FigureQrCode.vue b/admin/src/components/FigureQrCode.vue new file mode 100644 index 000000000..11a11d98c --- /dev/null +++ b/admin/src/components/FigureQrCode.vue @@ -0,0 +1,56 @@ + + + diff --git a/admin/src/components/Tables/OpenCreationsTable.spec.js b/admin/src/components/Tables/OpenCreationsTable.spec.js index 9ff348562..2b41a9b96 100644 --- a/admin/src/components/Tables/OpenCreationsTable.spec.js +++ b/admin/src/components/Tables/OpenCreationsTable.spec.js @@ -69,6 +69,7 @@ const propsData = { { key: 'edit_creation', label: 'edit' }, { key: 'confirm', label: 'save' }, ], + toggleDetails: false, } const mocks = { @@ -101,7 +102,7 @@ describe('OpenCreationsTable', () => { }) it('has a DIV element with the class .open-creations-table', () => { - expect(wrapper.find('div.open-creations-table').exists()).toBeTruthy() + expect(wrapper.find('div.open-creations-table').exists()).toBe(true) }) it('has a table with three rows', () => { @@ -109,7 +110,7 @@ describe('OpenCreationsTable', () => { }) it('find first button.bi-pencil-square for open EditCreationFormular ', () => { - expect(wrapper.findAll('tr').at(1).find('.bi-pencil-square').exists()).toBeTruthy() + expect(wrapper.findAll('tr').at(1).find('.bi-pencil-square').exists()).toBe(true) }) describe('show edit details', () => { @@ -122,7 +123,15 @@ describe('OpenCreationsTable', () => { }) it.skip('renders the component component-edit-creation-formular', () => { - expect(wrapper.find('div.component-edit-creation-formular').exists()).toBeTruthy() + expect(wrapper.find('div.component-edit-creation-formular').exists()).toBe(true) + }) + }) + + describe('call updateUserData', () => { + it('user creations has updated data', async () => { + wrapper.vm.updateUserData(propsData.items[0], [444, 555, 666]) + await wrapper.vm.$nextTick() + expect(wrapper.vm.items[0].creation).toEqual([444, 555, 666]) }) }) }) diff --git a/admin/src/components/Tables/OpenCreationsTable.vue b/admin/src/components/Tables/OpenCreationsTable.vue index d2e9669e6..1e61f00b0 100644 --- a/admin/src/components/Tables/OpenCreationsTable.vue +++ b/admin/src/components/Tables/OpenCreationsTable.vue @@ -70,12 +70,23 @@ export default { required: true, }, }, + data() { + return { + creationUserData: { + amount: null, + date: null, + memo: null, + moderator: null, + }, + } + }, methods: { updateCreationData(data) { - this.creationUserData.amount = data.amount - this.creationUserData.date = data.date - this.creationUserData.memo = data.memo - this.creationUserData.moderator = data.moderator + this.creationUserData = data + // this.creationUserData.amount = data.amount + // this.creationUserData.date = data.date + // this.creationUserData.memo = data.memo + // this.creationUserData.moderator = data.moderator data.row.toggleDetails() }, updateUserData(rowItem, newCreation) { diff --git a/admin/src/graphql/createContributionLink.js b/admin/src/graphql/createContributionLink.js new file mode 100644 index 000000000..fb6728243 --- /dev/null +++ b/admin/src/graphql/createContributionLink.js @@ -0,0 +1,27 @@ +import gql from 'graphql-tag' + +export const createContributionLink = gql` + mutation ( + $validFrom: String! + $validTo: String! + $name: String! + $amount: Decimal! + $memo: String! + $cycle: String! + $maxPerCycle: Int! = 1 + $maxAmountPerMonth: Decimal + ) { + createContributionLink( + validFrom: $validFrom + validTo: $validTo + name: $name + amount: $amount + memo: $memo + cycle: $cycle + maxPerCycle: $maxPerCycle + maxAmountPerMonth: $maxAmountPerMonth + ) { + link + } + } +` diff --git a/admin/src/graphql/deleteContributionLink.js b/admin/src/graphql/deleteContributionLink.js new file mode 100644 index 000000000..d0a938627 --- /dev/null +++ b/admin/src/graphql/deleteContributionLink.js @@ -0,0 +1,7 @@ +import gql from 'graphql-tag' + +export const deleteContributionLink = gql` + mutation ($id: Int!) { + deleteContributionLink(id: $id) + } +` diff --git a/admin/src/graphql/listContributionLinks.js b/admin/src/graphql/listContributionLinks.js new file mode 100644 index 000000000..7ce4b04e6 --- /dev/null +++ b/admin/src/graphql/listContributionLinks.js @@ -0,0 +1,23 @@ +import gql from 'graphql-tag' + +export const listContributionLinks = gql` + query ($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) { + listContributionLinks(currentPage: $currentPage, pageSize: $pageSize, order: $order) { + links { + id + amount + name + memo + code + link + createdAt + validFrom + validTo + maxAmountPerMonth + cycle + maxPerCycle + } + count + } + } +` diff --git a/admin/src/graphql/showContributionLink.js b/admin/src/graphql/showContributionLink.js new file mode 100644 index 000000000..8042db6b5 --- /dev/null +++ b/admin/src/graphql/showContributionLink.js @@ -0,0 +1,18 @@ +import gql from 'graphql-tag' + +export const showContributionLink = gql` + query ($id: Int!) { + showContributionLink { + id + validFrom + validTo + name + memo + amount + cycle + maxPerCycle + maxAmountPerMonth + code + } + } +` diff --git a/admin/src/locales/de.json b/admin/src/locales/de.json index b667a1ada..2256c1252 100644 --- a/admin/src/locales/de.json +++ b/admin/src/locales/de.json @@ -1,6 +1,35 @@ { "all_emails": "Alle Nutzer", "back": "zurück", + "contributionLink": { + "amount": "Betrag", + "clear": "Löschen", + "contributionLinks": "Beitragslinks", + "create": "Anlegen", + "cycle": "Zyklus", + "deleteNow": "Automatische Creations wirklich löschen?", + "maximumAmount": "maximaler Betrag", + "maxPerCycle": "Wiederholungen", + "memo": "Nachricht", + "name": "Name", + "newContributionLink": "Neuer Beitragslink", + "noContributionLinks": "Es sind keine Beitragslinks angelegt.", + "noDateSelected": "Kein Datum ausgewählt", + "noEndDate": "Kein Enddatum gewählt.", + "noStartDate": "Kein Startdatum gewählt.", + "options": { + "cycle": { + "daily": "täglich", + "hourly": "stündlich", + "monthly": "monatlich", + "once": "einmalig", + "weekly": "wöchentlich", + "yearly": "jährlich" + } + }, + "validFrom": "Startdatum", + "validTo": "Enddatum" + }, "creation": "Schöpfung", "creationList": "Schöpfungsliste", "creation_form": { @@ -44,7 +73,8 @@ "lastname": "Nachname", "math": { "exclaim": "!", - "pipe": "|" + "pipe": "|", + "plus": "+" }, "moderator": "Moderator", "multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.", diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json index 982c42d92..0c8cc8c62 100644 --- a/admin/src/locales/en.json +++ b/admin/src/locales/en.json @@ -1,6 +1,35 @@ { "all_emails": "All users", "back": "back", + "contributionLink": { + "amount": "Amount", + "clear": "Clear", + "contributionLinks": "Contribution Links", + "create": "Create", + "cycle": "Cycle", + "deleteNow": "Do you really delete automatic creations?", + "maximumAmount": "Maximum amount", + "maxPerCycle": "Repetition", + "memo": "Memo", + "name": "Name", + "newContributionLink": "New contribution link", + "noContributionLinks": "No contribution link has been created.", + "noDateSelected": "No date selected", + "noEndDate": "No end-date", + "noStartDate": "No start-date", + "options": { + "cycle": { + "daily": "daily", + "hourly": "hourly", + "monthly": "monthly", + "once": "once", + "weekly": "weekly", + "yearly": "yearly" + } + }, + "validFrom": "Start-date", + "validTo": "End-Date" + }, "creation": "Creation", "creationList": "Creation list", "creation_form": { @@ -44,7 +73,8 @@ "lastname": "Lastname", "math": { "exclaim": "!", - "pipe": "|" + "pipe": "|", + "plus": "+" }, "moderator": "Moderator", "multiple_creation_text": "Please select one or more members for which you would like to perform creations.", diff --git a/admin/src/pages/Overview.vue b/admin/src/pages/Overview.vue index 1ebb53540..69aa15c1a 100644 --- a/admin/src/pages/Overview.vue +++ b/admin/src/pages/Overview.vue @@ -28,13 +28,25 @@ + diff --git a/admin/yarn.lock b/admin/yarn.lock index 6eccc5003..09b543354 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -932,6 +932,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.11.2", "@babel/runtime@^7.16.0": + version "7.18.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" + integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.14.0": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" @@ -4397,7 +4404,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0, color-name@~1.1.4: +color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -4845,6 +4852,11 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssfontparser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3" + integrity sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg== + cssnano-preset-default@^4.0.0, cssnano-preset-default@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff" @@ -7821,6 +7833,14 @@ javascript-stringify@^2.0.1: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== +jest-canvas-mock@^2.3.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.4.0.tgz#947b71442d7719f8e055decaecdb334809465341" + integrity sha512-mmMpZzpmLzn5vepIaHk5HoH3Ka4WykbSoLuG/EKoJd0x0ID/t+INo1l8ByfcUJuDM+RIsL4QDg/gDnBbrj2/IQ== + dependencies: + cssfontparser "^1.2.1" + moo-color "^1.0.2" + jest-changed-files@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" @@ -9478,6 +9498,13 @@ mkdirp@0.x, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" +moo-color@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.3.tgz#d56435f8359c8284d83ac58016df7427febece74" + integrity sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ== + dependencies: + color-name "^1.1.4" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -10959,6 +10986,27 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +qrcanvas-vue@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/qrcanvas-vue/-/qrcanvas-vue-2.1.1.tgz#27b449f99eaf46f324b300215469bfdf8ef77d88" + integrity sha512-86NMjOJ5XJGrrqrD2t+zmZxZKNuW1Is7o88UOiM8qFxDBjuTyfq9VJE9/2rN5XxThsjBuY4bRrQqL9blVwnI9w== + dependencies: + "@babel/runtime" "^7.16.0" + qrcanvas "^3.1.2" + +qrcanvas@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/qrcanvas/-/qrcanvas-3.1.2.tgz#81a25e91b2c27e9ace91da95591cbfb100d68702" + integrity sha512-lNcAyCHN0Eno/mJ5eBc7lHV/5ejAJxII0UELthG3bNnlLR+u8hCc7CR+hXBawbYUf96kNIosXfG2cJzx92ZWKg== + dependencies: + "@babel/runtime" "^7.11.2" + qrcode-generator "^1.4.4" + +qrcode-generator@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/qrcode-generator/-/qrcode-generator-1.4.4.tgz#63f771224854759329a99048806a53ed278740e7" + integrity sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw== + qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"