fix(frontend): post migration fixes (#3372)

* fix(frontend): post migration fixes

* fix(frontend): align with stylelint

* fix(frontend): fix tests and dashboard layout

---------

Co-authored-by: einhornimmond <dario.rekowski@gmx.de>
This commit is contained in:
MateuszMichalowski 2024-10-10 09:46:12 +02:00 committed by GitHub
parent b8df97068f
commit 71f19d366f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 125 additions and 148 deletions

View File

@ -48,6 +48,7 @@
"portal-vue": "^3.0.0",
"qrcanvas-vue": "3",
"regenerator-runtime": "^0.13.7",
"tua-body-scroll-lock": "^1.5.1",
"uuid": "^9.0.0",
"vee-validate": "^4.13.2",
"vite": "3.2.10",

View File

@ -1,68 +1,5 @@
// import { shallowMount, RouterLinkStub } from '@vue/test-utils'
// import App from './App'
//
// const localVue = global.localVue
// const mockStoreCommit = jest.fn()
//
// const stubs = {
// RouterLink: RouterLinkStub,
// RouterView: true,
// }
//
// describe('App', () => {
// const mocks = {
// $i18n: {
// locale: 'en',
// },
// $t: jest.fn((t) => t),
// $store: {
// commit: mockStoreCommit,
// state: {
// token: null,
// },
// },
// $route: {
// meta: {
// requiresAuth: false,
// },
// params: {},
// },
// }
//
// let wrapper
//
// const Wrapper = () => {
// return shallowMount(App, { localVue, mocks, stubs })
// }
//
// describe('mount', () => {
// beforeEach(() => {
// wrapper = Wrapper()
// })
//
// it('renders the App', () => {
// expect(wrapper.find('#app').exists()).toBe(true)
// })
//
// it('has a component AuthLayout', () => {
// expect(wrapper.findComponent({ name: 'AuthLayout' }).exists()).toBe(true)
// })
//
// describe('route requires authorization', () => {
// beforeEach(async () => {
// mocks.$route.meta.requiresAuth = true
// wrapper = Wrapper()
// })
//
// it('has a component DashboardLayout', () => {
// expect(wrapper.findComponent({ name: 'DashboardLayout' }).exists()).toBe(true)
// })
// })
// })
// })
import { shallowMount } from '@vue/test-utils'
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { describe, it, expect, beforeEach } from 'vitest'
import App from './App'
import DashboardLayout from '@/layouts/DashboardLayout'
import AuthLayout from '@/layouts/AuthLayout'

View File

@ -345,3 +345,15 @@ a:hover,
.gdd-toaster-body {
color: rgb(255 255 255);
}
.gdd-toaster-body-darken {
color: #2c2c2c;
}
input.rounded-input {
border-radius: 17px;
}
.fs-7 {
font-size: 14px !important;
}

View File

@ -13,7 +13,7 @@
<div class="mb-3">
<BButton
v-if="!pending && transactionLinks.length < transactionLinkCount"
class="test-button-load-more"
class="test-button-load-more w-100 rounded-5"
block
variant="outline-primary"
@click.stop="loadMoreLinks"

View File

@ -64,11 +64,11 @@ describe('InputEmail', () => {
})
it('has the placeholder "input-field-placeholder"', () => {
expect(wrapper.find('input').attributes('placeholder')).toBe('input-field-placeholder')
expect(wrapper.find('input').attributes('placeholder')).toBe('form.email')
})
it('has the label "input-field-label"', () => {
expect(wrapper.find('label').text()).toBe('input-field-label')
expect(wrapper.find('label').text()).toBe('form.email')
})
it('has the label for "input-field-name-input-field"', () => {

View File

@ -11,6 +11,7 @@
:placeholder="defaultTranslations.placeholder"
type="email"
trim
class="rounded-input"
:class="$route.path === '/send' ? 'bg-248' : ''"
:disabled="disabled"
autocomplete="off"
@ -33,14 +34,6 @@ const props = defineProps({
type: String,
default: 'email',
},
label: {
type: String,
default: 'Email',
},
placeholder: {
type: String,
default: 'Email',
},
disabled: {
type: Boolean,
default: false,
@ -54,8 +47,8 @@ const { value, errorMessage, validate, meta } = useField(() => props.name, 'requ
const { t } = useI18n()
const defaultTranslations = computed(() => ({
label: props.label ?? t('form.email'),
placeholder: props.placeholder ?? t('form.email'),
label: t('form.email'),
placeholder: t('form.email'),
}))
const normalizeEmail = (emailAddress) => {

View File

@ -80,7 +80,7 @@ describe('InputPassword', () => {
})
it('has the placeholder "input-field-placeholder"', () => {
expect(wrapper.find('input').attributes('placeholder')).toEqual('input-field-placeholder')
expect(wrapper.find('input').attributes('placeholder')).toEqual('form.password')
})
it('has the value ""', () => {
@ -88,7 +88,7 @@ describe('InputPassword', () => {
})
it('has the label "input-field-label"', () => {
expect(wrapper.find('label').text()).toEqual('input-field-label')
expect(wrapper.find('label').text()).toEqual('form.password')
})
it('has the label for "input-field-name-input-field"', () => {

View File

@ -50,14 +50,6 @@ const props = defineProps({
type: String,
default: 'password',
},
label: {
type: String,
default: 'Password',
},
placeholder: {
type: String,
default: 'Password',
},
immediate: {
type: Boolean,
default: false,
@ -78,18 +70,11 @@ const { value, errorMessage, meta, errors, validate } = useField(name, props.rul
validateOnMount: props.immediate,
})
// onMounted(async () => {
// await nextTick()
// if (props.immediate) {
// await validate()
// }
// })
const { t } = useI18n()
const defaultTranslations = computed(() => ({
label: props.label ?? t('form.password'),
placeholder: props.placeholder ?? t('form.password'),
label: t('form.password'),
placeholder: t('form.password'),
}))
const showPassword = ref(false)
@ -109,3 +94,9 @@ const ariaMsg = computed(() => ({
const labelFor = computed(() => `${props.name}-input-field`)
</script>
<style scoped>
input {
border-radius: 17px 0 0 17px;
}
</style>

View File

@ -129,8 +129,9 @@ button.navbar-toggler > span.navbar-toggler-icon {
.navbar-element {
z-index: 1000;
position: fixed;
width: 100%;
background-color: #f5f5f5e6;
left: 0;
right: 0;
}
.sheet-img {

View File

@ -7,6 +7,7 @@
no-header-close
horizontal
skip-animation
@update:model-value="isMobileMenuOpen = $event"
>
<div class="mobile-sidebar-wrapper py-2">
<BImg src="img/svg/lines.png" />
@ -17,14 +18,30 @@
</template>
<script setup>
import { ref, watch } from 'vue'
import { lock, unlock } from 'tua-body-scroll-lock'
const isMobileMenuOpen = ref(false)
const emit = defineEmits(['admin', 'logout'])
watch(
() => isMobileMenuOpen.value,
(newVal) => {
if (newVal) {
lock()
} else {
unlock()
}
},
)
</script>
<style>
.mobile-sidebar-wrapper {
width: 220px;
background-color: #fff;
z-index: 10;
z-index: 1001;
position: absolute;
border-bottom-right-radius: 26px;
border-top-right-radius: 26px;
@ -38,6 +55,7 @@ const emit = defineEmits(['admin', 'logout'])
left: 0;
top: 0;
bottom: 0;
z-index: 1001;
}
.simple-overlay {
@ -46,7 +64,7 @@ const emit = defineEmits(['admin', 'logout'])
top: 0;
bottom: 0;
background-color: #212529;
z-index: 9;
z-index: 99;
opacity: 0.6;
width: calc(100vw - 200px);
}

View File

@ -1,6 +1,6 @@
<template>
<div class="transaction-link gradido-custom-background">
<BRow :class="validLink ? '' : 'bg-muted text-dark'" class="mb-2 pt-2 pb-2">
<BRow :class="{ 'light-gray-text': !validLink }" class="mb-2 pt-2 pb-2">
<BCol cols="1">
<variant-icon icon="link45deg" variant="danger" />
</BCol>
@ -153,4 +153,8 @@ const toggleQrModal = () => {
width: 1.5rem;
height: 1.5rem;
}
.light-gray-text {
color: #adb5bd !important;
}
</style>

View File

@ -1,4 +1,4 @@
import { mount } from '@vue/test-utils'
import { mount, RouterLinkStub } from '@vue/test-utils'
import { describe, it, expect, beforeEach, vi } from 'vitest'
import Name from './Name'
import { BLink } from 'bootstrap-vue-next'
@ -44,7 +44,7 @@ describe('Name', () => {
global: {
mocks,
stubs: {
BLink,
RouterLink: RouterLinkStub,
},
},
props: propsData,
@ -88,31 +88,18 @@ describe('Name', () => {
it('has a link', () => {
expect(
wrapper
.find('div.gdd-transaction-list-item-name')
.findComponent({ name: 'BLink' })
.exists(),
wrapper.find('div.gdd-transaction-list-item-name').findComponent(RouterLinkStub).exists(),
).toBe(true)
})
describe('click link', () => {
beforeEach(async () => {
await wrapper.findComponent({ name: 'BLink' }).trigger('click')
})
it('pushes router to send', () => {
expect(routerPushMock).toHaveBeenCalledWith({
path: '/send',
})
})
it('pushes params for gradidoID and community UUID', () => {
expect(routerPushMock).toHaveBeenCalledWith({
params: {
communityIdentifier: 'community UUID',
userIdentifier: 'gradido-ID',
},
})
it('RouterLink has correct to prop', () => {
const routerLink = wrapper.findComponent(RouterLinkStub)
expect(routerLink.props().to).toEqual({
name: 'Send',
params: {
communityIdentifier: 'community UUID',
userIdentifier: 'gradido-ID',
},
})
})
})

View File

@ -2,9 +2,9 @@
<div class="name">
<div class="gdd-transaction-list-item-name">
<div v-if="linkedUser && linkedUser.gradidoID">
<BLink :class="fontColor" @click.stop="tunnelEmail">
<router-link :class="fontColor" :to="pushTo">
{{ itemText }}
</BLink>
</router-link>
</div>
<span v-else>{{ itemText }}</span>
</div>
@ -45,6 +45,15 @@ export default {
(this.linkedUser.communityName ? ' / ' + this.linkedUser.communityName : '')
: this.text
},
pushTo() {
return {
name: 'Send',
params: {
userIdentifier: this.linkedUser.gradidoID,
communityIdentifier: this.linkedUser.communityUuid,
},
}
},
},
methods: {
async tunnelEmail() {

View File

@ -22,6 +22,7 @@ export function useAppToast() {
toast(message, {
title: t('navigation.info'),
variant: 'warning',
bodyClass: 'gdd-toaster-body-darken',
})
}

View File

@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils'
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
import { nextTick } from 'vue'
import { nextTick, ref } from 'vue'
import DashboardLayout from './DashboardLayout'
import { createStore } from 'vuex'
import { createRouter, createWebHistory } from 'vue-router'
@ -15,11 +15,15 @@ vi.mock('@/composables/useToast', () => ({
}))
const mockQueryFn = vi.fn()
const mockRefetchFn = vi.fn()
const mockMutateFn = vi.fn()
const mockQueryResult = ref(null)
vi.mock('@vue/apollo-composable', () => ({
useLazyQuery: vi.fn(() => ({
load: mockQueryFn,
refetch: mockRefetchFn,
result: mockQueryResult,
onResult: vi.fn(),
onError: vi.fn(),
})),
@ -136,25 +140,25 @@ describe('DashboardLayout', () => {
describe('update transactions', () => {
beforeEach(async () => {
mockQueryFn.mockResolvedValue({
mockQueryResult.value = {
transactionList: {
balance: {
balanceGDT: 100,
balanceGDT: '100',
count: 4,
linkCount: 8,
balance: 1450,
decay: 1250,
balance: '1450',
},
transactions: ['transaction', 'transaction', 'transaction', 'transaction'],
transactions: ['transaction1', 'transaction2', 'transaction3', 'transaction4'],
},
})
await wrapper
.findComponent({ ref: 'router-view' })
.vm.$emit('update-transactions', { currentPage: 2, pageSize: 5 })
await nextTick()
}
mockQueryFn.mockResolvedValue(mockQueryResult.value)
await wrapper.vm.updateTransactions({ currentPage: 2, pageSize: 5 })
await nextTick() // Ensure all promises are resolved
})
it('calls the API', () => {
it('load call to the API', () => {
expect(mockQueryFn).toHaveBeenCalled()
})
@ -164,10 +168,10 @@ describe('DashboardLayout', () => {
it('updates transactions', () => {
expect(wrapper.vm.transactions).toEqual([
'transaction',
'transaction',
'transaction',
'transaction',
'transaction1',
'transaction2',
'transaction3',
'transaction4',
])
})

View File

@ -215,7 +215,11 @@ import { useAppToast } from '@/composables/useToast'
const store = useStore()
const router = useRouter()
const { load: useCommunityStatsQuery } = useLazyQuery(communityStatistics)
const { load: useTransactionsQuery } = useLazyQuery(transactionsQuery)
const {
load: useTransactionsQuery,
refetch: useRefetchTransactionsQuery,
result: transactionQueryResult,
} = useLazyQuery(transactionsQuery)
const { mutate: useLogoutMutation } = useMutation(logout)
const { t } = useI18n()
const { toastError } = useAppToast()
@ -239,7 +243,7 @@ const testModal = () => {
}
onMounted(() => {
updateTransactions({ currentPage: 0, pageSize: 10 })
updateTransactions({ currentPage: 1, pageSize: 10 })
getCommunityStatistics()
setTimeout(() => {
skeleton.value = false
@ -260,9 +264,9 @@ const logoutUser = async () => {
const updateTransactions = async ({ currentPage, pageSize }) => {
pending.value = true
try {
const result = await useTransactionsQuery()
if (!result) return // TODO this return mitigate an error when this method is called second time but without actual request
const { transactionList } = result
await loadOrFetchTransactionQuery({ currentPage, pageSize })
if (!transactionQueryResult) return
const { transactionList } = transactionQueryResult.value
GdtBalance.value =
transactionList.balance.balanceGDT === null ? 0 : Number(transactionList.balance.balanceGDT)
transactions.value = transactionList.transactions
@ -277,6 +281,13 @@ const updateTransactions = async ({ currentPage, pageSize }) => {
}
}
const loadOrFetchTransactionQuery = async (queryVariables = { currentPage: 1, pageSize: 25 }) => {
return (
(await useTransactionsQuery(transactionsQuery, queryVariables)) ||
(await useRefetchTransactionsQuery(queryVariables))
)
}
const getCommunityStatistics = async () => {
try {
const result = await useCommunityStatsQuery()

View File

@ -19,10 +19,11 @@
</BCol>
</BRow>
<BRow>
<BCol cols="12" lg="6">
<BCol class="col-lg-6 col-12">
<BButton
ref="submitBtn"
type="submit"
class="w-100 fs-7"
:variant="meta.valid ? 'gradido' : 'gradido-disable'"
block
:disabled="!meta.valid"

View File

@ -23,6 +23,8 @@ const routes = [
// communityIdentifier can be community name or community UUID
path: '/send/:communityIdentifier?/:userIdentifier?',
component: () => import('@/pages/Send'),
name: 'Send',
props: true,
meta: {
requiresAuth: true,
pageTitle: 'send',

View File

@ -6772,6 +6772,11 @@ tslib@^2.1.0, tslib@^2.3.0, tslib@^2.6.2:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
tua-body-scroll-lock@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/tua-body-scroll-lock/-/tua-body-scroll-lock-1.5.1.tgz#1b8b7316dff55a821d5bec3fef045f995e7627a5"
integrity sha512-AOjusG9EjTGxqqL1xqg6JeMauJ+IQoX9ITW1qP7UugySUdH6lzi2CqJRmU+oYqOv7vCQjOs5CQrjIakGlbOenQ==
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"