diff --git a/admin/.dockerignore b/admin/.dockerignore index 88249539f..1ddda051a 100644 --- a/admin/.dockerignore +++ b/admin/.dockerignore @@ -1,3 +1,4 @@ node_modules .git -.gitignore \ No newline at end of file +.gitignore +!.eslintignore \ No newline at end of file diff --git a/admin/.eslintignore b/admin/.eslintignore index 433390392..94934f9e7 100644 --- a/admin/.eslintignore +++ b/admin/.eslintignore @@ -1,4 +1,3 @@ -node_modules -coverage -**/*.min.js -dist \ No newline at end of file +node_modules/ +dist/ +coverage/ \ No newline at end of file diff --git a/admin/.eslintrc.js b/admin/.eslintrc.js index 96c602ef3..73dce291f 100644 --- a/admin/.eslintrc.js +++ b/admin/.eslintrc.js @@ -8,9 +8,20 @@ module.exports = { parserOptions: { parser: 'babel-eslint', }, - extends: ['standard', 'plugin:vue/essential', 'plugin:prettier/recommended'], + extends: [ + 'standard', + 'plugin:vue/essential', + 'plugin:prettier/recommended', + 'plugin:@intlify/vue-i18n/recommended', + ], // required to lint *.vue files plugins: ['vue', 'prettier', 'jest'], + overrides: [ + { + files: ['*.json'], + extends: ['plugin:@intlify/vue-i18n/recommended'], + }, + ], // add your custom rules here rules: { 'no-console': ['error'], @@ -22,6 +33,17 @@ module.exports = { allowBinding: false, }, ], + '@intlify/vue-i18n/no-dynamic-keys': 'error', + '@intlify/vue-i18n/no-unused-keys': [ + 'error', + { + src: './src', + extensions: ['.js', '.vue'], + ignores: [], + enableFix: false, + }, + ], + '@intlify/vue-i18n/no-missing-keys-in-other-locales': 'error', 'prettier/prettier': [ 'error', { @@ -29,4 +51,12 @@ module.exports = { }, ], }, + settings: { + 'vue-i18n': { + localeDir: './src/locales/*.json', + // Specify the version of `vue-i18n` you are using. + // If not specified, the message will be parsed twice. + messageSyntaxVersion: '^8.26.5', + }, + }, } diff --git a/admin/package.json b/admin/package.json index a46dc1fd6..2b4be2851 100644 --- a/admin/package.json +++ b/admin/package.json @@ -12,11 +12,10 @@ "build": "vue-cli-service build", "dev": "yarn run serve", "analyse-bundle": "yarn build && webpack-bundle-analyzer dist/webpack.stats.json", - "lint": "eslint --max-warnings=0 --ext .js,.vue .", + "lint": "eslint --max-warnings=0 --ext .js,.vue,.json .", "stylelint": "stylelint --max-warnings=0 '**/*.{scss,vue}'", - "i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'", "test": "TZ=UTC jest --coverage", - "locales": "scripts/missing-keys.sh && scripts/sort.sh" + "locales": "scripts/sort.sh" }, "dependencies": { "@babel/core": "^7.15.8", @@ -52,6 +51,7 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.15.8", + "@intlify/eslint-plugin-vue-i18n": "^1.4.0", "@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-eslint": "~4.5.0", "@vue/cli-service": "~4.5.0", diff --git a/admin/src/components/ContentFooter.vue b/admin/src/components/ContentFooter.vue index 510318e61..c10e53596 100644 --- a/admin/src/components/ContentFooter.vue +++ b/admin/src/components/ContentFooter.vue @@ -4,24 +4,24 @@ diff --git a/admin/src/components/Overlay.vue b/admin/src/components/Overlay.vue index bf477c491..2a9bcd1ce 100644 --- a/admin/src/components/Overlay.vue +++ b/admin/src/components/Overlay.vue @@ -7,7 +7,7 @@ {{ $t('transactionlist.amount') }} - {{ item.amount }} GDD + {{ item.amount }} {{ $t('GDD') }} diff --git a/admin/src/components/Tables/SelectUsersTable.vue b/admin/src/components/Tables/SelectUsersTable.vue index 5bc49823a..73be770ed 100644 --- a/admin/src/components/Tables/SelectUsersTable.vue +++ b/admin/src/components/Tables/SelectUsersTable.vue @@ -12,7 +12,7 @@ > -
{{ $t('e_mail') }}!
+
{{ $t('e_mail') }}{{ $t('math.exclaim') }}
diff --git a/admin/src/locales/de.json b/admin/src/locales/de.json index 2a05f6ff0..fd902db97 100644 --- a/admin/src/locales/de.json +++ b/admin/src/locales/de.json @@ -1,8 +1,6 @@ { "all_emails": "Alle Nutzer", "back": "zurück", - "bookmark": "bookmark", - "confirmed": "bestätigt", "creation": "Schöpfung", "creation_form": { "creation_failed": "Ausstehende Schöpfung für {email} konnte nicht erzeugt werden.", @@ -16,7 +14,6 @@ "submit_creation": "Schöpfung einreichen", "toasted": "Offene Schöpfung ({value} GDD) für {email} wurde gespeichert und liegt zur Bestätigung bereit", "toasted_created": "Schöpfung wurde erfolgreich gespeichert", - "toasted_default": "`Fall {event} wird nicht unterstützt`", "toasted_delete": "Offene Schöpfung wurde gelöscht", "toasted_update": "`Offene Schöpfung {value} GDD) für {email} wurde geändert und liegt zur Bestätigung bereit", "update_creation": "Schöpfung aktualisieren" @@ -27,14 +24,26 @@ "deleted": "gelöscht", "deleted_user": "Alle gelöschten Nutzer", "delete_user": "Nutzer löschen", - "details": "Details", "edit": "Bearbeiten", + "enabled": "aktiviert", "error": "Fehler", "e_mail": "E-Mail", "firstname": "Vorname", - "gradido_admin_footer": "Gradido Akademie Adminkonsole", + "footer": { + "app_version": "App version {version}", + "copyright": { + "link": "Gradido Akademie Adminkonsole", + "year": "© {year}" + }, + "short_hash": "({shortHash})" + }, + "GDD": "GDD", "hide_details": "Details verbergen", "lastname": "Nachname", + "math": { + "exclaim": "!", + "pipe": "|" + }, "moderator": "Moderator", "multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.", "name": "Name", @@ -47,7 +56,6 @@ "user_search": "Nutzersuche" }, "not_open_creations": "Keine offenen Schöpfungen", - "open_creation": "Offene Schöpfung", "open_creations": "Offene Schöpfungen", "overlay": { "confirm": { @@ -56,13 +64,6 @@ "text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar und kann auch nicht mehr gelöscht werden. Bitte überprüfe genau, dass alles stimmt.", "title": "Schöpfung bestätigen!", "yes": "Ja, Schöpfung bestätigen und speichern!" - }, - "remove": { - "no": "Nein, nicht löschen.", - "question": "Willst du die vorgespeicherte Schöpfung wirklich löschen?", - "text": "Nach dem Löschen gibt es keine Möglichkeit mehr diesen Datensatz wiederherzustellen. Es wird aber der gesamte Vorgang in der Logdatei als Übersicht gespeichert.", - "title": "Achtung! Schöpfung löschen!", - "yes": "Ja, Schöpfung löschen!" } }, "remove": "Entfernen", @@ -72,13 +73,11 @@ "status": "Status", "success": "Erfolg", "text": "Text", - "transaction": "Transaktion", "transactionlist": { "amount": "Betrag", "balanceDate": "Schöpfungsdatum", "community": "Gemeinschaft", "date": "Datum", - "decay": "Vergänglichkeit", "memo": "Nachricht", "title": "Alle geschöpften Transaktionen für den Nutzer" }, diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json index 898c49ded..d91922d3b 100644 --- a/admin/src/locales/en.json +++ b/admin/src/locales/en.json @@ -1,8 +1,6 @@ { "all_emails": "All users", "back": "back", - "bookmark": "Remember", - "confirmed": "confirmed", "creation": "Creation", "creation_form": { "creation_failed": "Could not create pending creation for {email}", @@ -16,7 +14,6 @@ "submit_creation": "Submit creation", "toasted": "Open creation ({value} GDD) for {email} has been saved and is ready for confirmation.", "toasted_created": "Creation has been successfully saved", - "toasted_default": "`Case {event} is not supported`", "toasted_delete": "Open creation has been deleted", "toasted_update": "Open creation {value} GDD) for {email} has been changed and is ready for confirmation.", "update_creation": "Creation update" @@ -27,14 +24,26 @@ "deleted": "deleted", "deleted_user": "All deleted user", "delete_user": "Delete user", - "details": "Details", "edit": "Edit", + "enabled": "enabled", "error": "Error", "e_mail": "E-mail", "firstname": "Firstname", - "gradido_admin_footer": "Gradido Academy Admin Console", + "footer": { + "app_version": "App version {version}", + "copyright": { + "link": "Gradido Academy Admin Console", + "year": "© {year}" + }, + "short_hash": "({shortHash})" + }, + "GDD": "GDD", "hide_details": "Hide details", "lastname": "Lastname", + "math": { + "exclaim": "!", + "pipe": "|" + }, "moderator": "Moderator", "multiple_creation_text": "Please select one or more members for which you would like to perform creations.", "name": "Name", @@ -47,7 +56,6 @@ "user_search": "User search" }, "not_open_creations": "No open creations", - "open_creation": "Open creation", "open_creations": "Open creations", "overlay": { "confirm": { @@ -56,13 +64,6 @@ "text": "After saving, the record can no longer be changed or deleted. Please check carefully that everything is correct.", "title": "Confirm creation!", "yes": "Yes, confirm and save creation!" - }, - "remove": { - "no": "No, do not delete.", - "question": "Do you really want to delete the pre-stored creation?", - "text": "After deletion, there is no possibility to restore this data record. However, the entire process is saved in the log file as an overview.", - "title": "Attention! Delete creation!", - "yes": "Yes, delete creation!" } }, "remove": "Remove", @@ -72,13 +73,11 @@ "status": "Status", "success": "Success", "text": "Text", - "transaction": "Transaction", "transactionlist": { "amount": "Amount", "balanceDate": "Creation date", "community": "Community", "date": "Date", - "decay": "Decay", "memo": "Message", "title": "All creation-transactions for the user" }, diff --git a/admin/src/pages/Creation.vue b/admin/src/pages/Creation.vue index a5966ee68..8a229d7be 100644 --- a/admin/src/pages/Creation.vue +++ b/admin/src/pages/Creation.vue @@ -2,7 +2,7 @@
- + { }) return forward(operation).map((response) => { if (response.errors && response.errors[0].message === '403.13 - Client certificate revoked') { - response.errors[0].message = i18n.t('error.session-expired') store.dispatch('logout', null) window.location.assign(CONFIG.WALLET_URL) return response diff --git a/admin/vue.config.js b/admin/vue.config.js index 18929c38c..4492312a0 100644 --- a/admin/vue.config.js +++ b/admin/vue.config.js @@ -14,6 +14,7 @@ module.exports = { fallbackLocale: 'de', localeDir: 'locales', enableInSFC: false, + enableLegacy: false, }, }, lintOnSave: true, diff --git a/admin/yarn.lock b/admin/yarn.lock index 4dd2c8193..d5fae27fe 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -932,6 +932,13 @@ 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" + integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@7", "@babel/template@^7.0.0", "@babel/template@^7.15.4", "@babel/template@^7.3.3", "@babel/template@^7.4.0": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -1001,6 +1008,21 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" + integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.1" + globals "^13.9.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -1042,6 +1064,83 @@ cssnano-preset-default "^4.0.0" postcss "^7.0.0" +"@intlify/core-base@^9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.1.9.tgz#e4e8c951010728e4af3a0d13d74cf3f9e7add7f6" + integrity sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw== + dependencies: + "@intlify/devtools-if" "9.1.9" + "@intlify/message-compiler" "9.1.9" + "@intlify/message-resolver" "9.1.9" + "@intlify/runtime" "9.1.9" + "@intlify/shared" "9.1.9" + "@intlify/vue-devtools" "9.1.9" + +"@intlify/devtools-if@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.1.9.tgz#a30e1dd1256ff2c5c98d8d75d075384fba898e5d" + integrity sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ== + dependencies: + "@intlify/shared" "9.1.9" + +"@intlify/eslint-plugin-vue-i18n@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@intlify/eslint-plugin-vue-i18n/-/eslint-plugin-vue-i18n-1.4.0.tgz#f8fe791892c2dce7d189a364b6a908c87e1c3ac9" + integrity sha512-anB1eBf6rpxpWyW883gi6O1hozQy4Q02VyzyodOUnohOqT07GATVSxnr2J9/qQSV47xWukV+9LiRErJcU7d/uA== + dependencies: + "@eslint/eslintrc" "^1.2.0" + "@intlify/core-base" "^9.1.9" + "@intlify/message-compiler" "^9.1.9" + debug "^4.3.1" + glob "^7.1.3" + ignore "^5.0.5" + is-language-code "^3.1.0" + js-yaml "^4.0.0" + json5 "^2.1.3" + jsonc-eslint-parser "^2.0.0" + lodash "^4.17.11" + parse5 "^6.0.0" + semver "^7.3.4" + vue-eslint-parser "^8.0.0" + yaml-eslint-parser "^0.5.0" + +"@intlify/message-compiler@9.1.9", "@intlify/message-compiler@^9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.1.9.tgz#1193cbd224a71c2fb981455b8534a3c766d2948d" + integrity sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ== + dependencies: + "@intlify/message-resolver" "9.1.9" + "@intlify/shared" "9.1.9" + source-map "0.6.1" + +"@intlify/message-resolver@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/message-resolver/-/message-resolver-9.1.9.tgz#3155ccd2f5e6d0dc16cad8b7f1d8e97fcda05bfc" + integrity sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA== + +"@intlify/runtime@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/runtime/-/runtime-9.1.9.tgz#2c12ce29518a075629efed0a8ed293ee740cb285" + integrity sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg== + dependencies: + "@intlify/message-compiler" "9.1.9" + "@intlify/message-resolver" "9.1.9" + "@intlify/shared" "9.1.9" + +"@intlify/shared@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.1.9.tgz#0baaf96128b85560666bec784ffb01f6623cc17a" + integrity sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw== + +"@intlify/vue-devtools@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz#2be8f4dbe7f7ed4115676eb32348141d411e426b" + integrity sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og== + dependencies: + "@intlify/message-resolver" "9.1.9" + "@intlify/runtime" "9.1.9" + "@intlify/shared" "9.1.9" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -2399,6 +2498,11 @@ acorn@^8.2.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== +acorn@^8.5.0, acorn@^8.7.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + address@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -2645,6 +2749,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -4892,7 +5001,7 @@ debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.3.0, debug@^4.3.3: +debug@^4.3.0, debug@^4.3.1, 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== @@ -5615,6 +5724,14 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" @@ -5644,6 +5761,11 @@ eslint-visitor-keys@^3.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz#e32e99c6cdc2eb063f204eda5db67bfe58bb4186" integrity sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q== +eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + eslint@7.25.0: version "7.25.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.25.0.tgz#1309e4404d94e676e3e831b3a3ad2b050031eb67" @@ -5710,6 +5832,15 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" +espree@^9.0.0, espree@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== + dependencies: + acorn "^8.7.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^3.3.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -7003,16 +7134,16 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.0.5, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + ignore@^5.1.1, ignore@^5.1.4: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== -ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -7386,6 +7517,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-language-code@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-language-code/-/is-language-code-3.1.0.tgz#b2386b49227e7010636f16d0c2c681ca40136ab5" + integrity sha512-zJdQ3QTeLye+iphMeK3wks+vXSRFKh68/Pnlw7aOfApFSEIOhYa8P9vwwa6QrImNNBMJTiL1PpYF0f4BxDuEgA== + dependencies: + "@babel/runtime" "^7.14.0" + is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -8571,6 +8709,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.0.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -8723,7 +8868,7 @@ json3@^3.3.3: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== -json5@2.x, json5@^2.1.2: +json5@2.x, json5@^2.1.2, json5@^2.1.3: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== @@ -8742,6 +8887,16 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +jsonc-eslint-parser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.1.0.tgz#4c126b530aa583d85308d0b3041ff81ce402bbb2" + integrity sha512-qCRJWlbP2v6HbmKW7R3lFbeiVWHo+oMJ0j+MizwvauqnCV/EvtAeEeuCgoc/ErtsuoKgYB8U4Ih8AxJbXoE6/g== + dependencies: + acorn "^8.5.0" + eslint-visitor-keys "^3.0.0" + espree "^9.0.0" + semver "^7.3.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -10014,7 +10169,7 @@ parse5@5.1.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== -parse5@6.0.1, parse5@^6.0.1: +parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== @@ -11700,16 +11855,16 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - source-map@^0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" @@ -12970,6 +13125,19 @@ vue-eslint-parser@^7.10.0: lodash "^4.17.21" semver "^6.3.0" +vue-eslint-parser@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz#5d31129a1b3dd89c0069ca0a1c88f970c360bd0d" + integrity sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g== + dependencies: + debug "^4.3.2" + eslint-scope "^7.0.0" + eslint-visitor-keys "^3.1.0" + espree "^9.0.0" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.5" + vue-functional-data-merge@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz#08a7797583b7f35680587f8a1d51d729aa1dc657" @@ -13515,7 +13683,16 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: +yaml-eslint-parser@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/yaml-eslint-parser/-/yaml-eslint-parser-0.5.0.tgz#01d4e4d992a820769ea85ef5fd526dfc20ebc6f5" + integrity sha512-nJeyLA3YHAzhBTZbRAbu3W6xrSCucyxExmA+ZDtEdUFpGllxAZpto2Zxo2IG0r0eiuEiBM4e+wiAdxTziTq94g== + dependencies: + eslint-visitor-keys "^3.0.0" + lodash "^4.17.21" + yaml "^1.10.2" + +yaml@^1.10.0, yaml@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== diff --git a/backend/jest.config.js b/backend/jest.config.js index 41e464b8e..497b4b389 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -3,7 +3,7 @@ module.exports = { verbose: true, preset: 'ts-jest', collectCoverage: true, - collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'], + collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**'], setupFiles: ['/test/testSetup.ts'], moduleNameMapper: { '@/(.*)': '/src/$1', diff --git a/backend/package.json b/backend/package.json index 79e5fd130..8ef168454 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,7 +13,8 @@ "start": "node build/src/index.js", "dev": "nodemon -w src --ext ts --exec ts-node src/index.ts", "lint": "eslint --max-warnings=0 --ext .js,.ts .", - "test": "TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles" + "test": "TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles", + "seed": "TZ=UTC ts-node src/seeds/index.ts" }, "dependencies": { "@types/jest": "^27.0.2", @@ -42,6 +43,7 @@ }, "devDependencies": { "@types/express": "^4.17.12", + "@types/faker": "^5.5.9", "@types/jsonwebtoken": "^8.5.2", "@types/node": "^16.10.3", "@types/nodemailer": "^6.4.4", @@ -54,6 +56,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^3.4.0", "eslint-plugin-promise": "^5.1.0", + "faker": "^5.5.3", "nodemon": "^2.0.7", "prettier": "^2.3.1", "ts-node": "^10.0.0", diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 947636aa4..61625a8ae 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1,9 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { testEnvironment, createUser, headerPushMock, cleanDB, resetToken } from '@test/helpers' -import { createUserMutation, setPasswordMutation } from '@test/graphql' -import gql from 'graphql-tag' +import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/helpers' +import { createUserFactory } from '@/seeds/factory/user' +import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' +import { createUser, setPassword } from '@/seeds/graphql/mutations' +import { login, logout } from '@/seeds/graphql/queries' import { GraphQLError } from 'graphql' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User } from '@entity/User' @@ -11,8 +13,6 @@ import CONFIG from '@/config' import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' // import { klicktippSignIn } from '@/apis/KlicktippController' -jest.setTimeout(1000000) - jest.mock('@/mailer/sendAccountActivationEmail', () => { return { __esModule: true, @@ -31,24 +31,6 @@ jest.mock('@/apis/KlicktippController', () => { let mutate: any, query: any, con: any -const loginQuery = gql` - query ($email: String!, $password: String!, $publisherId: Int) { - login(email: $email, password: $password, publisherId: $publisherId) { - email - firstName - lastName - language - coinanimation - klickTipp { - newsletterState - } - hasElopage - publisherId - isAdmin - } - } -` - beforeAll(async () => { const testEnv = await testEnvironment() mutate = testEnv.mutate @@ -77,7 +59,7 @@ describe('UserResolver', () => { beforeAll(async () => { jest.clearAllMocks() - result = await mutate({ mutation: createUserMutation, variables }) + result = await mutate({ mutation: createUser, variables }) }) afterAll(async () => { @@ -149,7 +131,7 @@ describe('UserResolver', () => { describe('email already exists', () => { it('throws an error', async () => { - await expect(mutate({ mutation: createUserMutation, variables })).resolves.toEqual( + await expect(mutate({ mutation: createUser, variables })).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('User already exists.')], }), @@ -160,7 +142,7 @@ describe('UserResolver', () => { describe('unknown language', () => { it('sets "de" as default language', async () => { await mutate({ - mutation: createUserMutation, + mutation: createUser, variables: { ...variables, email: 'bibi@bloxberg.de', language: 'es' }, }) await expect(User.find()).resolves.toEqual( @@ -177,7 +159,7 @@ describe('UserResolver', () => { describe('no publisher id', () => { it('sets publisher id to null', async () => { await mutate({ - mutation: createUserMutation, + mutation: createUser, variables: { ...variables, email: 'raeuber@hotzenplotz.de', publisherId: undefined }, }) await expect(User.find()).resolves.toEqual( @@ -208,11 +190,11 @@ describe('UserResolver', () => { let newUser: any beforeAll(async () => { - await mutate({ mutation: createUserMutation, variables: createUserVariables }) + await mutate({ mutation: createUser, variables: createUserVariables }) const loginEmailOptIn = await LoginEmailOptIn.find() emailOptIn = loginEmailOptIn[0].verificationCode.toString() result = await mutate({ - mutation: setPasswordMutation, + mutation: setPassword, variables: { code: emailOptIn, password: 'Aa12345_' }, }) newUser = await User.find() @@ -252,11 +234,11 @@ describe('UserResolver', () => { describe('no valid password', () => { beforeAll(async () => { - await mutate({ mutation: createUserMutation, variables: createUserVariables }) + await mutate({ mutation: createUser, variables: createUserVariables }) const loginEmailOptIn = await LoginEmailOptIn.find() emailOptIn = loginEmailOptIn[0].verificationCode.toString() result = await mutate({ - mutation: setPasswordMutation, + mutation: setPassword, variables: { code: emailOptIn, password: 'not-valid' }, }) }) @@ -280,9 +262,9 @@ describe('UserResolver', () => { describe('no valid optin code', () => { beforeAll(async () => { - await mutate({ mutation: createUserMutation, variables: createUserVariables }) + await mutate({ mutation: createUser, variables: createUserVariables }) result = await mutate({ - mutation: setPasswordMutation, + mutation: setPassword, variables: { code: 'not valid', password: 'Aa12345_' }, }) }) @@ -303,7 +285,7 @@ describe('UserResolver', () => { describe('login', () => { const variables = { - email: 'peter@lustig.de', + email: 'bibi@bloxberg.de', password: 'Aa12345_', publisherId: 1234, } @@ -316,7 +298,7 @@ describe('UserResolver', () => { describe('no users in database', () => { beforeAll(async () => { - result = await query({ query: loginQuery, variables }) + result = await query({ query: login, variables }) }) it('throws an error', () => { @@ -330,14 +312,8 @@ describe('UserResolver', () => { describe('user is in database and correct login data', () => { beforeAll(async () => { - await createUser(mutate, { - email: 'peter@lustig.de', - firstName: 'Peter', - lastName: 'Lustig', - language: 'de', - publisherId: 1234, - }) - result = await query({ query: loginQuery, variables }) + await createUserFactory(mutate, bibiBloxberg) + result = await query({ query: login, variables }) }) afterAll(async () => { @@ -350,15 +326,15 @@ describe('UserResolver', () => { data: { login: { coinanimation: true, - email: 'peter@lustig.de', - firstName: 'Peter', + email: 'bibi@bloxberg.de', + firstName: 'Bibi', hasElopage: false, isAdmin: false, klickTipp: { newsletterState: false, }, language: 'de', - lastName: 'Lustig', + lastName: 'Bloxberg', publisherId: 1234, }, }, @@ -373,13 +349,7 @@ describe('UserResolver', () => { describe('user is in database and wrong password', () => { beforeAll(async () => { - await createUser(mutate, { - email: 'peter@lustig.de', - firstName: 'Peter', - lastName: 'Lustig', - language: 'de', - publisherId: 1234, - }) + await createUserFactory(mutate, bibiBloxberg) }) afterAll(async () => { @@ -388,7 +358,7 @@ describe('UserResolver', () => { it('returns an error', () => { expect( - query({ query: loginQuery, variables: { ...variables, password: 'wrong' } }), + query({ query: login, variables: { ...variables, password: 'wrong' } }), ).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('No user with this credentials')], @@ -399,16 +369,10 @@ describe('UserResolver', () => { }) describe('logout', () => { - const logoutQuery = gql` - query { - logout - } - ` - describe('unauthenticated', () => { it('throws an error', async () => { resetToken() - await expect(query({ query: logoutQuery })).resolves.toEqual( + await expect(query({ query: logout })).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('401 Unauthorized')], }), @@ -418,19 +382,13 @@ describe('UserResolver', () => { describe('authenticated', () => { const variables = { - email: 'peter@lustig.de', + email: 'bibi@bloxberg.de', password: 'Aa12345_', } beforeAll(async () => { - await createUser(mutate, { - email: 'peter@lustig.de', - firstName: 'Peter', - lastName: 'Lustig', - language: 'de', - publisherId: 1234, - }) - await query({ query: loginQuery, variables }) + await createUserFactory(mutate, bibiBloxberg) + await query({ query: login, variables }) }) afterAll(async () => { @@ -438,7 +396,7 @@ describe('UserResolver', () => { }) it('returns true', async () => { - await expect(query({ query: logoutQuery })).resolves.toEqual( + await expect(query({ query: logout })).resolves.toEqual( expect.objectContaining({ data: { logout: 'true' }, errors: undefined, diff --git a/backend/src/seeds/factory/user.ts b/backend/src/seeds/factory/user.ts new file mode 100644 index 000000000..89bff6852 --- /dev/null +++ b/backend/src/seeds/factory/user.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { createUser, setPassword } from '@/seeds/graphql/mutations' +import { User } from '@entity/User' +import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' +import { ServerUser } from '@entity/ServerUser' +import { UserInterface } from '@/seeds/users/UserInterface' + +export const createUserFactory = async (mutate: any, user: UserInterface): Promise => { + await mutate({ mutation: createUser, variables: user }) + let dbUser = await User.findOneOrFail({ where: { email: user.email } }) + + if (user.emailChecked) { + const optin = await LoginEmailOptIn.findOneOrFail({ where: { userId: dbUser.id } }) + await mutate({ + mutation: setPassword, + variables: { password: 'Aa12345_', code: optin.verificationCode }, + }) + } + + // refetch data + dbUser = await User.findOneOrFail({ where: { email: user.email } }) + + if (user.createdAt || user.deletedAt) { + if (user.createdAt) dbUser.createdAt = user.createdAt + if (user.deletedAt) dbUser.deletedAt = user.deletedAt + await dbUser.save() + } + + if (user.isAdmin) { + const admin = new ServerUser() + admin.username = dbUser.firstName + admin.password = 'please_refactor' + admin.email = dbUser.email + admin.role = 'admin' + admin.activated = 1 + admin.lastLogin = new Date() + admin.created = dbUser.createdAt + admin.modified = dbUser.createdAt + await admin.save() + } +} diff --git a/backend/src/seeds/graphql/enums.ts b/backend/src/seeds/graphql/enums.ts new file mode 100644 index 000000000..6e40dd19c --- /dev/null +++ b/backend/src/seeds/graphql/enums.ts @@ -0,0 +1,9 @@ +export const GdtEntryType = { + FORM: 'FORM', + CVS: 'CVS', + ELOPAGE: 'ELOPAGE', + ELOPAGE_PUBLISHER: 'ELOPAGE_PUBLISHER', + DIGISTORE: 'DIGISTORE', + CVS2: 'CVS2', + GLOBAL_MODIFICATOR: 'GLOBAL_MODIFICATOR', +} diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts new file mode 100644 index 000000000..8f425cdec --- /dev/null +++ b/backend/src/seeds/graphql/mutations.ts @@ -0,0 +1,71 @@ +import gql from 'graphql-tag' + +export const subscribeNewsletter = gql` + mutation ($email: String!, $language: String!) { + subscribeNewsletter(email: $email, language: $language) + } +` + +export const unsubscribeNewsletter = gql` + mutation ($email: String!) { + unsubscribeNewsletter(email: $email) + } +` + +export const setPassword = gql` + mutation ($code: String!, $password: String!) { + setPassword(code: $code, password: $password) + } +` + +export const updateUserInfos = gql` + mutation ( + $firstName: String + $lastName: String + $password: String + $passwordNew: String + $locale: String + $coinanimation: Boolean + ) { + updateUserInfos( + firstName: $firstName + lastName: $lastName + password: $password + passwordNew: $passwordNew + language: $locale + coinanimation: $coinanimation + ) + } +` + +export const createUser = gql` + mutation ( + $firstName: String! + $lastName: String! + $email: String! + $language: String! + $publisherId: Int + ) { + createUser( + email: $email + firstName: $firstName + lastName: $lastName + language: $language + publisherId: $publisherId + ) + } +` + +export const sendCoins = gql` + mutation ($email: String!, $amount: Decimal!, $memo: String!) { + sendCoins(email: $email, amount: $amount, memo: $memo) + } +` + +export const createTransactionLink = gql` + mutation ($amount: Decimal!, $memo: String!) { + createTransactionLink(amount: $amount, memo: $memo) { + code + } + } +` diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts new file mode 100644 index 000000000..942258fde --- /dev/null +++ b/backend/src/seeds/graphql/queries.ts @@ -0,0 +1,144 @@ +import gql from 'graphql-tag' + +export const login = gql` + query ($email: String!, $password: String!, $publisherId: Int) { + login(email: $email, password: $password, publisherId: $publisherId) { + email + firstName + lastName + language + coinanimation + klickTipp { + newsletterState + } + hasElopage + publisherId + isAdmin + } + } +` + +export const verifyLogin = gql` + query { + verifyLogin { + email + firstName + lastName + language + coinanimation + klickTipp { + newsletterState + } + hasElopage + publisherId + isAdmin + } + } +` + +export const logout = gql` + query { + logout + } +` + +export const transactionsQuery = gql` + query ( + $currentPage: Int = 1 + $pageSize: Int = 25 + $order: Order = DESC + $onlyCreations: Boolean = false + ) { + transactionList( + currentPage: $currentPage + pageSize: $pageSize + order: $order + onlyCreations: $onlyCreations + ) { + balanceGDT + count + balance + decayStartBlock + transactions { + id + typeId + amount + balance + balanceDate + memo + linkedUser { + firstName + lastName + } + decay { + decay + start + end + duration + } + } + } + } +` + +export const sendResetPasswordEmail = gql` + query ($email: String!) { + sendResetPasswordEmail(email: $email) + } +` + +export const listGDTEntriesQuery = gql` + query ($currentPage: Int!, $pageSize: Int!) { + listGDTEntries(currentPage: $currentPage, pageSize: $pageSize) { + count + gdtEntries { + id + amount + date + comment + gdtEntryType + factor + gdt + } + gdtSum + } + } +` + +export const communityInfo = gql` + query { + getCommunityInfo { + name + description + registerUrl + url + } + } +` + +export const communities = gql` + query { + communities { + id + name + url + description + registerUrl + } + } +` + +export const queryTransactionLink = gql` + query ($code: String!) { + queryTransactionLink(code: $code) { + amount + memo + createdAt + validUntil + user { + firstName + publisherId + } + } + } +` diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts new file mode 100644 index 000000000..b68bcbdd9 --- /dev/null +++ b/backend/src/seeds/index.ts @@ -0,0 +1,64 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import createServer from '../server/createServer' +import { createTestClient } from 'apollo-server-testing' + +import { name, internet, random } from 'faker' + +import { users } from './users/index' +import { createUserFactory } from './factory/user' +import { entities } from '@entity/index' + +const context = { + token: '', + setHeaders: { + push: (value: string): void => { + context.token = value + }, + // eslint-disable-next-line @typescript-eslint/no-empty-function + forEach: (): void => {}, + }, +} + +export const cleanDB = async () => { + // this only works as lond we do not have foreign key constraints + for (let i = 0; i < entities.length; i++) { + await resetEntity(entities[i]) + } +} + +const resetEntity = async (entity: any) => { + const items = await entity.find({ withDeleted: true }) + if (items.length > 0) { + const ids = items.map((i: any) => i.id) + await entity.delete(ids) + } +} + +const run = async () => { + const server = await createServer(context) + const testClient = createTestClient(server.apollo) + const { mutate } = testClient + const { con } = server + await cleanDB() + + // seed the standard users + for (let i = 0; i < users.length; i++) { + await createUserFactory(mutate, users[i]) + } + + // seed 100 random users + for (let i = 0; i < 100; i++) { + await createUserFactory(mutate, { + firstName: name.firstName(), + lastName: name.lastName(), + email: internet.email(), + language: random.boolean() ? 'en' : 'de', + }) + } + + await con.close() +} + +run() diff --git a/backend/src/seeds/users/UserInterface.ts b/backend/src/seeds/users/UserInterface.ts new file mode 100644 index 000000000..08aa5d19d --- /dev/null +++ b/backend/src/seeds/users/UserInterface.ts @@ -0,0 +1,12 @@ +export interface UserInterface { + email?: string + firstName?: string + lastName?: string + // description?: string + createdAt?: Date + emailChecked?: boolean + language?: string + deletedAt?: Date + publisherId?: number + isAdmin?: boolean +} diff --git a/backend/src/seeds/users/bibi-bloxberg.ts b/backend/src/seeds/users/bibi-bloxberg.ts new file mode 100644 index 000000000..7c372848e --- /dev/null +++ b/backend/src/seeds/users/bibi-bloxberg.ts @@ -0,0 +1,11 @@ +import { UserInterface } from './UserInterface' + +export const bibiBloxberg: UserInterface = { + email: 'bibi@bloxberg.de', + firstName: 'Bibi', + lastName: 'Bloxberg', + // description: 'Hex Hex', + emailChecked: true, + language: 'de', + publisherId: 1234, +} diff --git a/backend/src/seeds/users/bob-baumeister.ts b/backend/src/seeds/users/bob-baumeister.ts new file mode 100644 index 000000000..ac88611b8 --- /dev/null +++ b/backend/src/seeds/users/bob-baumeister.ts @@ -0,0 +1,10 @@ +import { UserInterface } from './UserInterface' + +export const bobBaumeister: UserInterface = { + email: 'bob@baumeister.de', + firstName: 'Bob', + lastName: 'der Baumeister', + // description: 'Können wir das schaffen? Ja, wir schaffen das!', + emailChecked: true, + language: 'de', +} diff --git a/backend/src/seeds/users/garrick-ollivander.ts b/backend/src/seeds/users/garrick-ollivander.ts new file mode 100644 index 000000000..264a866bd --- /dev/null +++ b/backend/src/seeds/users/garrick-ollivander.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const garrickOllivander: UserInterface = { + email: 'garrick@ollivander.com', + firstName: 'Garrick', + lastName: 'Ollivander', + // description: `Curious ... curious ... + // Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`, + createdAt: new Date('2022-01-10T10:23:17'), + emailChecked: false, + language: 'en', +} diff --git a/backend/src/seeds/users/index.ts b/backend/src/seeds/users/index.ts new file mode 100644 index 000000000..7a6dbe519 --- /dev/null +++ b/backend/src/seeds/users/index.ts @@ -0,0 +1,15 @@ +import { peterLustig } from './peter-lustig' +import { bibiBloxberg } from './bibi-bloxberg' +import { bobBaumeister } from './bob-baumeister' +import { raeuberHotzenplotz } from './raeuber-hotzenplotz' +import { stephenHawking } from './stephen-hawking' +import { garrickOllivander } from './garrick-ollivander' + +export const users = [ + peterLustig, + bibiBloxberg, + bobBaumeister, + raeuberHotzenplotz, + stephenHawking, + garrickOllivander, +] diff --git a/backend/src/seeds/users/peter-lustig.ts b/backend/src/seeds/users/peter-lustig.ts new file mode 100644 index 000000000..0cdc67829 --- /dev/null +++ b/backend/src/seeds/users/peter-lustig.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const peterLustig: UserInterface = { + email: 'peter@lustig.de', + firstName: 'Peter', + lastName: 'Lustig', + // description: 'Latzhose und Nickelbrille', + createdAt: new Date('2020-11-25T10:48:43'), + emailChecked: true, + language: 'de', + isAdmin: true, +} diff --git a/backend/src/seeds/users/raeuber-hotzenplotz.ts b/backend/src/seeds/users/raeuber-hotzenplotz.ts new file mode 100644 index 000000000..62601efff --- /dev/null +++ b/backend/src/seeds/users/raeuber-hotzenplotz.ts @@ -0,0 +1,10 @@ +import { UserInterface } from './UserInterface' + +export const raeuberHotzenplotz: UserInterface = { + email: 'raeuber@hotzenplotz.de', + firstName: 'Räuber', + lastName: 'Hotzenplotz', + // description: 'Pfefferpistole', + emailChecked: true, + language: 'de', +} diff --git a/backend/src/seeds/users/stephen-hawking.ts b/backend/src/seeds/users/stephen-hawking.ts new file mode 100644 index 000000000..a683b7579 --- /dev/null +++ b/backend/src/seeds/users/stephen-hawking.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const stephenHawking: UserInterface = { + email: 'stephen@hawking.uk', + firstName: 'Stephen', + lastName: 'Hawking', + // description: 'A Brief History of Time', + emailChecked: true, + createdAt: new Date('1942-01-08T09:17:52'), + deletedAt: new Date('2018-03-14T09:17:52'), + language: 'en', +} diff --git a/backend/test/graphql.ts b/backend/test/graphql.ts deleted file mode 100644 index 89393876e..000000000 --- a/backend/test/graphql.ts +++ /dev/null @@ -1,25 +0,0 @@ -import gql from 'graphql-tag' - -export const createUserMutation = gql` - mutation ( - $email: String! - $firstName: String! - $lastName: String! - $language: String! - $publisherId: Int - ) { - createUser( - email: $email - firstName: $firstName - lastName: $lastName - language: $language - publisherId: $publisherId - ) - } -` - -export const setPasswordMutation = gql` - mutation ($code: String!, $password: String!) { - setPassword(code: $code, password: $password) - } -` diff --git a/backend/test/helpers.ts b/backend/test/helpers.ts index edb4eb3e4..51610b07e 100644 --- a/backend/test/helpers.ts +++ b/backend/test/helpers.ts @@ -4,9 +4,6 @@ import { createTestClient } from 'apollo-server-testing' import createServer from '../src/server/createServer' import { initialize } from '@dbTools/helpers' -import { createUserMutation, setPasswordMutation } from './graphql' -import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' -import { User } from '@entity/User' import { entities } from '@entity/index' export const headerPushMock = jest.fn((t) => { @@ -46,19 +43,6 @@ export const resetEntity = async (entity: any) => { } } -export const createUser = async (mutate: any, user: any) => { - // resetToken() - await mutate({ mutation: createUserMutation, variables: user }) - const dbUser = await User.findOne({ where: { email: user.email } }) - if (!dbUser) throw new Error('Ups, no user found') - const optin = await LoginEmailOptIn.findOne({ where: { userId: dbUser.id } }) - if (!optin) throw new Error('Ups, no optin found') - await mutate({ - mutation: setPasswordMutation, - variables: { password: 'Aa12345_', code: optin.verificationCode }, - }) -} - export const resetToken = () => { context.token = '' } diff --git a/backend/test/testSetup.ts b/backend/test/testSetup.ts index ed2c5cf49..d42836626 100644 --- a/backend/test/testSetup.ts +++ b/backend/test/testSetup.ts @@ -4,3 +4,4 @@ // eslint-disable-next-line @typescript-eslint/no-empty-function console.info = () => {} +jest.setTimeout(1000000) diff --git a/backend/yarn.lock b/backend/yarn.lock index 330a6d590..ee6b2d679 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -811,6 +811,11 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/faker@^5.5.9": + version "5.5.9" + resolved "https://registry.yarnpkg.com/@types/faker/-/faker-5.5.9.tgz#588ede92186dc557bff8341d294335d50d255f0c" + integrity sha512-uCx6mP3UY5SIO14XlspxsGjgaemrxpssJI0Ol+GfhxtcKpv9pgRZYsS4eeKeHVLje6Qtc8lGszuBI461+gVZBA== + "@types/fs-capacitor@*": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/fs-capacitor/-/fs-capacitor-2.0.0.tgz#17113e25817f584f58100fb7a08eed288b81956e" @@ -2510,6 +2515,11 @@ express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +faker@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" + integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 88249539f..1ddda051a 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -1,3 +1,4 @@ node_modules .git -.gitignore \ No newline at end of file +.gitignore +!.eslintignore \ No newline at end of file diff --git a/frontend/.eslintignore b/frontend/.eslintignore index e973d3709..94934f9e7 100644 --- a/frontend/.eslintignore +++ b/frontend/.eslintignore @@ -1,3 +1,3 @@ -node_modules -**/*.min.js -dist \ No newline at end of file +node_modules/ +dist/ +coverage/ \ No newline at end of file diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 96c602ef3..4e45ede62 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -8,9 +8,24 @@ module.exports = { parserOptions: { parser: 'babel-eslint', }, - extends: ['standard', 'plugin:vue/essential', 'plugin:prettier/recommended'], + extends: [ + 'standard', + 'plugin:vue/essential', + 'plugin:prettier/recommended', + 'plugin:@intlify/vue-i18n/recommended', + ], // required to lint *.vue files plugins: ['vue', 'prettier', 'jest'], + overrides: [ + { + files: ['*.json'], + extends: ['plugin:@intlify/vue-i18n/recommended'], + rules: { + // TODO: enable + '@intlify/vue-i18n/no-html-messages': 'off', + }, + }, + ], // add your custom rules here rules: { 'no-console': ['error'], @@ -22,6 +37,29 @@ module.exports = { allowBinding: false, }, ], + '@intlify/vue-i18n/no-dynamic-keys': 'error', + '@intlify/vue-i18n/no-unused-keys': [ + 'error', + { + src: './src', + extensions: ['.js', '.vue'], + // TODO: remove ignores + ignores: [ + '/site.thx./', + '/form./', + '/time./', + '/decay.types./', + 'settings.password.resend_subtitle', + 'settings.password.reset-password.text', + 'settings.password.set', + 'settings.password.set-password.text', + 'settings.password.subtitle', + 'site.login.signin', + ], + enableFix: false, + }, + ], + '@intlify/vue-i18n/no-missing-keys-in-other-locales': 'error', 'prettier/prettier': [ 'error', { @@ -29,4 +67,12 @@ module.exports = { }, ], }, + settings: { + 'vue-i18n': { + localeDir: './src/locales/*.json', + // Specify the version of `vue-i18n` you are using. + // If not specified, the message will be parsed twice. + messageSyntaxVersion: '^8.22.4', + }, + }, } diff --git a/frontend/.gitignore b/frontend/.gitignore index b868f0a64..b19667d17 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -5,7 +5,6 @@ dist/ npm-debug.log* yarn-debug.log* yarn-error.log* -test/unit/coverage package-lock.json /.env diff --git a/frontend/package.json b/frontend/package.json index 69c843bf5..6ccf37d09 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,11 +8,10 @@ "build": "vue-cli-service build", "dev": "yarn run serve", "analyse-bundle": "yarn build && webpack-bundle-analyzer dist/webpack.stats.json", - "lint": "eslint --max-warnings=0 --ext .js,.vue .", + "lint": "eslint --max-warnings=0 --ext .js,.vue,.json .", "stylelint": "stylelint --max-warnings=0 '**/*.{scss,vue}'", - "i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'", "test": "TZ=UTC jest --coverage", - "locales": "scripts/missing-keys.sh && scripts/sort.sh" + "locales": "scripts/sort.sh" }, "dependencies": { "@babel/core": "^7.13.13", @@ -67,6 +66,7 @@ "vuex-persistedstate": "^4.0.0-beta.3" }, "devDependencies": { + "@intlify/eslint-plugin-vue-i18n": "^1.4.0", "@vue/cli-plugin-babel": "^3.7.0", "@vue/cli-plugin-eslint": "^3.7.0", "@vue/cli-service": "^3.7.0", diff --git a/frontend/public/json-example/admin_card_statistic.json b/frontend/public/json-example/admin_card_statistic.json deleted file mode 100644 index bacdccb5a..000000000 --- a/frontend/public/json-example/admin_card_statistic.json +++ /dev/null @@ -1,36 +0,0 @@ -{"state":"success", "statisticdata": - [ - { - "i_creation_sum": 29288278.4653, - "i_creation_mon": 68278.4653, - "i_creation_365d": 19288278.4653, - "i_creation_30d": 288278.4653, - "i_creation_14d": 88278.4653, - "i_creation_7d": 3278.4653 - }, - { - "i_transience_sum": 8278.4653, - "i_transience_mon": 8278.4653, - "i_transience_365d": 8278.4653, - "i_transience_30d": 178.4653, - "i_transience_14d": 78.4653, - "i_transience_7d": 8.4653 - }, - { - "i_exchange_sum": 23345.2324, - "i_exchange_mon": 2, - "i_exchange_365d": 2, - "i_exchange_30d": 2, - "i_exchange_14d": 2, - "i_exchange_7d": 2 - }, - { - "i_members_sum": 5398, - "i_members_mon": 234, - "i_members_365d": 2356, - "i_members_30d": 123, - "i_members_14d": 23, - "i_members_7d": 24 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_charts_statistic.json b/frontend/public/json-example/admin_charts_statistic.json deleted file mode 100644 index 8c7e6667b..000000000 --- a/frontend/public/json-example/admin_charts_statistic.json +++ /dev/null @@ -1,64 +0,0 @@ -{"state":"success", "statisticdata": - [ - { - "charts_creation_0_mon": 5635, - "charts_creation_1_mon": 5635, - "charts_creation_2_mon": 5635, - "charts_creation_3_mon": 5635, - "charts_creation_4_mon": 5635, - "charts_creation_5_mon": 5635, - "charts_creation_6_mon": 5635, - "charts_creation_7_mon": 5635, - "charts_creation_8_mon": 5635, - "charts_creation_9_mon": 5635, - "charts_creation_10_mon": 5635, - "charts_creation_11_mon": 5635, - "charts_creation_12_mon": 5635 - }, - { - "charts_transience_0_mon": 5635, - "charts_transience_1_mon": 5635, - "charts_transience_2_mon": 5635, - "charts_transience_3_mon": 5635, - "charts_transience_4_mon": 5635, - "charts_transience_5_mon": 5635, - "charts_transience_6_mon": 5635, - "charts_transience_7_mon": 5635, - "charts_transience_8_mon": 5635, - "charts_transience_9_mon": 5635, - "charts_transience_10_mon": 5635, - "charts_transience_11_mon": 5635, - "charts_transience_12_mon": 5635 - }, - { - "charts_exchange_0_mon": 5635, - "charts_exchange_1_mon": 5635, - "charts_exchange_2_mon": 5635, - "charts_exchange_3_mon": 5635, - "charts_exchange_4_mon": 5635, - "charts_exchange_5_mon": 5635, - "charts_exchange_6_mon": 5635, - "charts_exchange_7_mon": 5635, - "charts_exchange_8_mon": 5635, - "charts_exchange_9_mon": 5635, - "charts_exchange_10_mon": 5635, - "charts_exchange_11_mon": 5635, - "charts_exchange_12_mon": 5635 - }, - { - "charts_members_0_mon": 5635, - "charts_members_1_mon": 5635, - "charts_members_2_mon": 5635, - "charts_members_3_mon": 5635, - "charts_members_4_mon": 5635, - "charts_members_5_mon": 5635, - "charts_members_6_mon": 5635, - "charts_members_7_mon": 5635, - "charts_members_8_mon": 5635, - "charts_members_9_mon": 5635, - "charts_members_10_mon": 5635, - "charts_members_11_mon": 5635, - "charts_members_12_mon": 5635 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_community_statistic.json b/frontend/public/json-example/admin_community_statistic.json deleted file mode 100644 index f083991aa..000000000 --- a/frontend/public/json-example/admin_community_statistic.json +++ /dev/null @@ -1,19 +0,0 @@ -{"state":"success", "statisticdata": - [ - { - "community_entries1_mon": 5635, - "community_entries2_mon": 5635, - "community_entries3_mon": 5635, - "community_entries4_mon": 5635, - "community_entries5_mon": 5635, - "community_entries6_mon": 5635, - "community_entries7_mon": 5635, - "community_entries8_mon": 5635, - "community_entries9_mon": 5635, - "community_entries0_mon": 5635, - "community_entries10_mon": 5635, - "community_entries11_mon": 5635, - "community_entries12_mon": 5635 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_transaction_list.json b/frontend/public/json-example/admin_transaction_list.json deleted file mode 100644 index a1af47db8..000000000 --- a/frontend/public/json-example/admin_transaction_list.json +++ /dev/null @@ -1,61 +0,0 @@ -{"state":"success", "transactions": - [ - { - "name": "Jon Tester", - "email": "jon@example.de", - "type": "send", - "transaction_id": 12, - "date": "29-11-2020", - "balance": 7000, - "memo": "Reperatur Waschbecken", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Gradido Community", - "email": "gradido@example.de", - "type": "creation", - "transaction_id": 11, - "date": "1-11-2020", - "balance": 10000, - "memo": "Müll gesammelt im Wald", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Maria Tester", - "email": "maria@example.de", - "type": "receive", - "transaction_id": 7, - "date": "23-10-2020", - "balance": 5000, - "memo": "Spende an Alice ", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Alice Tester", - "email": "alice@example.de", - "type": "receive", - "transaction_id": 5, - "date": "2-8-2020", - "balance": 1000, - "memo": "Bob hat meinen Müll getrennt", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Gradido Community", - "email": "gradido@example.de", - "type": "creation", - "transaction_id": 1, - "date": "11-7-2020", - "balance": 10000, - "memo": "Bob hat meinen Müll getrennt", - "pubkey": "abcdefghi123456789" - - } - ], - "transactionExecutingCount": 8750, - "count": 5 -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_transience_list.json b/frontend/public/json-example/admin_transience_list.json deleted file mode 100644 index 0cbacfbd8..000000000 --- a/frontend/public/json-example/admin_transience_list.json +++ /dev/null @@ -1,49 +0,0 @@ -{"state":"success", "participation": - [ - { - "name": "", - "type": "submitted", - "participation_id": 412, - "date_submitted": "9-12-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "8-12-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "name": "", - "type": "in progress", - "participation_id": 312, - "date_submitted": "2-11-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "2-11-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "name": "", - "type": "confirmed", - "participation_id": 212, - "date_submitted": "20-10-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "20-10-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "name": "", - "type": "rejected", - "participation_id": 142, - "date_submitted": "17-9-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "17-9-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - } - ], - "count": 4 -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_userlist.json b/frontend/public/json-example/admin_userlist.json deleted file mode 100644 index a8550716f..000000000 --- a/frontend/public/json-example/admin_userlist.json +++ /dev/null @@ -1,52 +0,0 @@ -{"state":"success", "admin0userlist": - [ - { - "created": 1578688666, - "disabled": false, - "email": "dervommond@gmail.com", - "email_checked": true, - "first_name": "Max", - "group_alias": "gdd1", - "ident_hash": 2928827813, - "last_name": "Miau", - "public_hex": "2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b", - "role": "admin", - "username": "", - "balance_gdd": 174500, - "balance_gdt": 4500, - "errorCount": 0 - }, - { - "created": 1578685678, - "disabled": false, - "email": "ttwer@gmail.com", - "email_checked": true, - "first_name": "John", - "group_alias": "gdd1", - "ident_hash": 2928827813, - "last_name": "Doe", - "public_hex": "2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71x", - "role": "user", - "username": "", - "balance_gdd": 144500, - "balance_gdt": 0, - "errorCount": 0 - }, - { - "created": 1578635671, - "disabled": false, - "email": "test@gmail.com", - "email_checked": true, - "first_name": "Alice", - "group_alias": "gdd1", - "ident_hash": 4928827813, - "last_name": "Seer", - "public_hex": "2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71a", - "role": "user", - "username": "", - "balance_gdd": 444500, - "balance_gdt": 4500, - "errorCount": 0 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/example.json b/frontend/public/json-example/example.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/public/json-example/userdata.json b/frontend/public/json-example/userdata.json deleted file mode 100644 index 69f5a8041..000000000 --- a/frontend/public/json-example/userdata.json +++ /dev/null @@ -1,19 +0,0 @@ -{"state":"success", "userdata": - [ - { - "created": 1578688666, - "disabled": false, - "email": "dervommond@gmail.com", - "email_checked": true, - "first_name": "Max", - "group_alias": "gdd1", - "ident_hash": 2928827813, - "last_name": "Miau", - "public_hex": "2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b", - "role": "admin", - "username": "", - "balance": 174500, - "errorCount": 0 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/userparticipation.json b/frontend/public/json-example/userparticipation.json deleted file mode 100644 index 4e29f117b..000000000 --- a/frontend/public/json-example/userparticipation.json +++ /dev/null @@ -1,45 +0,0 @@ -{"state":"success", "participation": - [ - { - "type": "submitted", - "participation_id": 412, - "date_submitted": "9-12-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "8-12-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "type": "in progress", - "participation_id": 312, - "date_submitted": "2-11-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "2-11-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "type": "confirmed", - "participation_id": 212, - "date_submitted": "20-10-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "20-10-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "type": "rejected", - "participation_id": 142, - "date_submitted": "17-9-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "17-9-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - } - ], - "count": 4 -} \ No newline at end of file diff --git a/frontend/public/json-example/usertransactions.json b/frontend/public/json-example/usertransactions.json deleted file mode 100644 index a1af47db8..000000000 --- a/frontend/public/json-example/usertransactions.json +++ /dev/null @@ -1,61 +0,0 @@ -{"state":"success", "transactions": - [ - { - "name": "Jon Tester", - "email": "jon@example.de", - "type": "send", - "transaction_id": 12, - "date": "29-11-2020", - "balance": 7000, - "memo": "Reperatur Waschbecken", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Gradido Community", - "email": "gradido@example.de", - "type": "creation", - "transaction_id": 11, - "date": "1-11-2020", - "balance": 10000, - "memo": "Müll gesammelt im Wald", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Maria Tester", - "email": "maria@example.de", - "type": "receive", - "transaction_id": 7, - "date": "23-10-2020", - "balance": 5000, - "memo": "Spende an Alice ", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Alice Tester", - "email": "alice@example.de", - "type": "receive", - "transaction_id": 5, - "date": "2-8-2020", - "balance": 1000, - "memo": "Bob hat meinen Müll getrennt", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Gradido Community", - "email": "gradido@example.de", - "type": "creation", - "transaction_id": 1, - "date": "11-7-2020", - "balance": 10000, - "memo": "Bob hat meinen Müll getrennt", - "pubkey": "abcdefghi123456789" - - } - ], - "transactionExecutingCount": 8750, - "count": 5 -} \ No newline at end of file diff --git a/frontend/scripts/missing-keys.sh b/frontend/scripts/missing-keys.sh deleted file mode 100755 index 6dbaee897..000000000 --- a/frontend/scripts/missing-keys.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -ROOT_DIR=$(dirname "$0")/.. - -sorting="jq -f $ROOT_DIR/scripts/sort_filter.jq" -english="$sorting $ROOT_DIR/src/locales/en.json" -german="$sorting $ROOT_DIR/src/locales/de.json" -listPaths="jq -c 'path(..)|[.[]|tostring]|join(\".\")'" -diffString="<( $english | $listPaths ) <( $german | $listPaths )" -if eval "diff -q $diffString"; -then - : # all good -else - eval "diff -y $diffString | grep '[|<>]'"; - printf "\nEnglish and German translation keys do not match, see diff above.\n" - exit 1 -fi diff --git a/admin/scripts/missing-keys.sh b/frontend/scripts/missing-keys.sh.old similarity index 100% rename from admin/scripts/missing-keys.sh rename to frontend/scripts/missing-keys.sh.old diff --git a/frontend/src/layouts/ContentFooter.spec.js b/frontend/src/components/ContentFooter.spec.js similarity index 92% rename from frontend/src/layouts/ContentFooter.spec.js rename to frontend/src/components/ContentFooter.spec.js index 4b360d581..f67560e44 100644 --- a/frontend/src/layouts/ContentFooter.spec.js +++ b/frontend/src/components/ContentFooter.spec.js @@ -11,7 +11,7 @@ describe('ContentFooter', () => { $i18n: { locale: 'en', }, - $t: jest.fn((t) => t), + $t: jest.fn((t, options) => (options ? [t, options] : t)), } const Wrapper = () => { @@ -33,11 +33,11 @@ describe('ContentFooter', () => { }) it('renders the copyright year', () => { - expect(wrapper.find('div.copyright').text()).toMatch(/©\s*2[0-9]{3,3}\s+/) + expect(mocks.$t).toBeCalledWith('footer.copyright.year', { year: 2022 }) }) it('renders a link to Gradido-Akademie', () => { - expect(wrapper.find('div.copyright').find('a').text()).toEqual('Gradido-Akademie') + expect(wrapper.find('div.copyright').find('a').text()).toEqual('footer.copyright.link') }) it('links to the login page when clicked on copyright', () => { @@ -51,7 +51,7 @@ describe('ContentFooter', () => { it('shows the current version', async () => { wrapper.setData({ version: 1.23 }) await wrapper.vm.$nextTick() - expect(wrapper.find('div.copyright').findAll('a').at(1).text()).toEqual('App version 1.23') + expect(mocks.$t).toBeCalledWith('footer.app_version', { version: 1.23 }) }) it('links to latest release on GitHub', () => { @@ -64,7 +64,7 @@ describe('ContentFooter', () => { wrapper.setData({ shortHash: 'ACCEDED' }) wrapper.setData({ hash: 'ACCEDEDC001D00DC001D00DC001D00DC001CAFA' }) await wrapper.vm.$nextTick() - expect(wrapper.find('div.copyright').findAll('a').at(2).text()).toEqual('(ACCEDED)') + expect(mocks.$t).toBeCalledWith('footer.short_hash', { shortHash: 'ACCEDED' }) }) it('links to last release commit', async () => { @@ -78,7 +78,7 @@ describe('ContentFooter', () => { describe('links to gradido.net', () => { it('has a link to the legal notice', () => { - expect(wrapper.findAll('a.nav-link').at(0).text()).toEqual('imprint') + expect(wrapper.findAll('a.nav-link').at(0).text()).toEqual('footer.imprint') }) it('links to the https://gradido.net/en/impressum when locale is en', () => { @@ -88,7 +88,7 @@ describe('ContentFooter', () => { }) it('has a link to the privacy policy', () => { - expect(wrapper.findAll('a.nav-link').at(1).text()).toEqual('privacy_policy') + expect(wrapper.findAll('a.nav-link').at(1).text()).toEqual('footer.privacy_policy') }) it('links to the https://gradido.net/en/datenschutz when locale is en', () => { @@ -98,7 +98,7 @@ describe('ContentFooter', () => { }) it('has a link to the members area', () => { - expect(wrapper.findAll('a.nav-link').at(2).text()).toEqual('members_area') + expect(wrapper.findAll('a.nav-link').at(2).text()).toEqual('navigation.members_area') }) it('links to the elopage', () => { diff --git a/frontend/src/layouts/ContentFooter.vue b/frontend/src/components/ContentFooter.vue similarity index 81% rename from frontend/src/layouts/ContentFooter.vue rename to frontend/src/components/ContentFooter.vue index de49e8635..bdcb5b1a9 100755 --- a/frontend/src/layouts/ContentFooter.vue +++ b/frontend/src/components/ContentFooter.vue @@ -3,24 +3,24 @@ @@ -29,16 +29,16 @@ - {{ $t('imprint') }} + {{ $t('footer.imprint') }} - {{ $t('privacy_policy') }} + {{ $t('footer.privacy_policy') }} - {{ $t('members_area') }} + {{ $t('navigation.members_area') }} - {{ $t('whitepaper') }} + {{ $t('footer.whitepaper') }} - {{ $t('site.navbar.support') }} + {{ $t('navigation.support') }} diff --git a/frontend/src/components/DecayInformations/DecayInformation-Decay.vue b/frontend/src/components/DecayInformations/DecayInformation-Decay.vue index 19aac9e98..7377b6fa8 100644 --- a/frontend/src/components/DecayInformations/DecayInformation-Decay.vue +++ b/frontend/src/components/DecayInformations/DecayInformation-Decay.vue @@ -14,7 +14,7 @@
{{ (Number(balance) - Number(decay.decay)) | GDD }} - {{ decay.decay | GDD }} = + {{ decay.decay | GDD }} {{ $t('math.equal') }} {{ balance | GDD }}
diff --git a/frontend/src/components/DecayInformations/DecayInformation-DecayStartblock.vue b/frontend/src/components/DecayInformations/DecayInformation-DecayStartblock.vue index 2ee9ecc2c..5802bfb4b 100644 --- a/frontend/src/components/DecayInformations/DecayInformation-DecayStartblock.vue +++ b/frontend/src/components/DecayInformations/DecayInformation-DecayStartblock.vue @@ -4,12 +4,11 @@
{{ $t('decay.Starting_block_decay') }}
-
{{ $t('decay.decay_introduced') }} :
+
{{ $t('decay.decay_introduced') }}
{{ $d(new Date(decay.start), 'long') }} - {{ $i18n.locale === 'de' ? 'Uhr' : '' }}
@@ -28,7 +27,8 @@
- {{ $t(`decay.${typeId.toLowerCase()}`) }} + + {{ $t(`decay.types.${typeId.toLowerCase()}`) }} {{ amount | GDD }} diff --git a/frontend/src/components/DecayInformations/DecayInformation-Long.vue b/frontend/src/components/DecayInformations/DecayInformation-Long.vue index b5e1bbbe5..50e3d6697 100644 --- a/frontend/src/components/DecayInformations/DecayInformation-Long.vue +++ b/frontend/src/components/DecayInformations/DecayInformation-Long.vue @@ -15,7 +15,6 @@
{{ $d(new Date(decay.start), 'long') }} - {{ $i18n.locale === 'de' ? 'Uhr' : '' }}
@@ -44,7 +43,8 @@
- {{ $t(`decay.${typeId.toLowerCase()}`) }} + + {{ $t(`decay.types.${typeId.toLowerCase()}`) }} {{ amount | GDD }} @@ -82,7 +82,8 @@ export default { const result = [] order.forEach((timeSpan) => { if (this.duration[timeSpan] > 0) { - const locale = this.$t(`decay.${timeSpan}`) + // eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys + const locale = this.$t(`time.${timeSpan}`) result.push(`${this.duration[timeSpan]} ${locale}`) } }) diff --git a/frontend/src/components/GddSend/QrCode.vue b/frontend/src/components/GddSend/QrCode.vue index 0146621ed..32e527cfe 100644 --- a/frontend/src/components/GddSend/QrCode.vue +++ b/frontend/src/components/GddSend/QrCode.vue @@ -1,5 +1,6 @@