Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions core/src/components/AccountMenu/AccountMenuProfileEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
{{ name }}
</template>
<template v-if="canCreateAppToken" #extra-actions>
<NcButton variant="secondary" @click="handleQrCodeClick">
<NcButton
:aria-label="t('core', 'Show QR code for mobile app login')"
variant="secondary"
@click="handleQrCodeClick">
<template #icon>
<IconQrcodeScan :size="20" />
</template>
Expand All @@ -36,6 +39,7 @@ import axios from '@nextcloud/axios'
import { getCapabilities } from '@nextcloud/capabilities'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { loadState } from '@nextcloud/initial-state'
import { t } from '@nextcloud/l10n'
import { addPasswordConfirmationInterceptors, PwdConfirmationMode } from '@nextcloud/password-confirmation'
import { generateUrl } from '@nextcloud/router'
import { spawnDialog } from '@nextcloud/vue/functions/dialog'
Expand Down Expand Up @@ -88,8 +92,9 @@ export default defineComponent({
setup() {
return {
canCreateAppToken,
profileEnabled,
displayName: getCurrentUser()!.displayName,
profileEnabled,
t,
}
},

Expand Down
132 changes: 132 additions & 0 deletions core/src/tests/components/AccountMenuProfileEntry.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const capabilities = vi.hoisted(() => ({
getCapabilities: vi.fn(),
}))
vi.mock('@nextcloud/capabilities', () => capabilities)

vi.mock('@nextcloud/auth', () => ({
getCurrentUser: () => ({ uid: 'user', displayName: 'User' }),
}))

vi.mock('@nextcloud/axios', () => ({ default: { post: vi.fn() } }))

vi.mock('@nextcloud/event-bus', () => ({
subscribe: vi.fn(),
unsubscribe: vi.fn(),
}))

vi.mock('@nextcloud/initial-state', () => ({
loadState: vi.fn((_app: string, key: string, fallback: unknown) => {
if (key === 'profileEnabled') {
return { profileEnabled: false }
}
return fallback
}),
}))

vi.mock('@nextcloud/l10n', () => ({
getLanguage: () => 'en',
t: (_app: string, text: string) => text,
}))

vi.mock('@nextcloud/password-confirmation', () => ({
addPasswordConfirmationInterceptors: vi.fn(),
PwdConfirmationMode: {
Strict: 0,
},
}))

vi.mock('@nextcloud/router', () => ({
generateUrl: (path: string) => path,
}))

vi.mock('@nextcloud/vue/components/NcButton', () => ({
default: {
name: 'NcButton',
inheritAttrs: false,
render(h) {
return h('button', {
attrs: this.$attrs,
on: {
click: (event: MouseEvent) => this.$emit('click', event),
},
}, this.$slots.icon)
},
},
}))

vi.mock('@nextcloud/vue/components/NcListItem', () => ({
default: {
name: 'NcListItem',
render(h) {
return h('li', [
this.$slots.subname,
this.$slots['extra-actions'],
this.$slots.indicator,
])
},
},
}))

vi.mock('@nextcloud/vue/components/NcLoadingIcon', () => ({
default: {
name: 'NcLoadingIcon',
render(h) {
return h('span')
},
},
}))

vi.mock('@nextcloud/vue/functions/dialog', () => ({
spawnDialog: vi.fn(),
}))

vi.mock('../../components/AccountMenu/AccountQRLoginDialog.vue', () => ({
default: {
name: 'AccountQRLoginDialog',
render(h) {
return h('div')
},
},
}))

vi.mock('vue-material-design-icons/QrcodeScan.vue', () => ({
default: {
name: 'IconQrcodeScan',
render(h) {
return h('span')
},
},
}))

describe('core: AccountMenuProfileEntry', () => {
beforeEach(() => {
vi.resetModules()
capabilities.getCapabilities.mockReturnValue({
core: {
'can-create-app-token': true,
},
})
})

it('labels the QR code button for assistive technologies', async () => {
const AccountMenuProfileEntry = (await import('../../components/AccountMenu/AccountMenuProfileEntry.vue')).default
const wrapper = mount(AccountMenuProfileEntry, {
propsData: {
id: 'profile',
name: 'Profile',
href: '/settings/user',
active: false,
},
})

expect(wrapper.get('button').attributes('aria-label')).toBe('Show QR code for mobile app login')
})
})
Loading