From bbbf140b1f8c2d0c2d8061ee26b67171ea036680 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Tue, 6 May 2025 19:04:25 -0700 Subject: [PATCH] Handle user avatar error (#3735) Co-authored-by: github-actions --- src/components/common/UserAvatar.test.ts | 106 ++++++++++++++++++ src/components/common/UserAvatar.vue | 25 +++++ .../dialog/content/setting/UserPanel.vue | 7 +- src/components/topbar/CurrentUserButton.vue | 9 +- src/components/topbar/CurrentUserPopover.vue | 12 +- src/locales/en/main.json | 1 + src/locales/es/main.json | 3 +- src/locales/fr/main.json | 3 +- src/locales/ja/main.json | 3 +- src/locales/ko/main.json | 3 +- src/locales/ru/main.json | 3 +- src/locales/zh/main.json | 3 +- 12 files changed, 155 insertions(+), 23 deletions(-) create mode 100644 src/components/common/UserAvatar.test.ts create mode 100644 src/components/common/UserAvatar.vue diff --git a/src/components/common/UserAvatar.test.ts b/src/components/common/UserAvatar.test.ts new file mode 100644 index 000000000..d844277b9 --- /dev/null +++ b/src/components/common/UserAvatar.test.ts @@ -0,0 +1,106 @@ +import { mount } from '@vue/test-utils' +import Avatar from 'primevue/avatar' +import PrimeVue from 'primevue/config' +import { beforeEach, describe, expect, it } from 'vitest' +import { createApp, nextTick } from 'vue' +import { createI18n } from 'vue-i18n' + +import UserAvatar from './UserAvatar.vue' + +const i18n = createI18n({ + legacy: false, + locale: 'en', + messages: { + en: { + auth: { + login: { + userAvatar: 'User Avatar' + } + } + } + } +}) + +describe('UserAvatar', () => { + beforeEach(() => { + const app = createApp({}) + app.use(PrimeVue) + }) + + const mountComponent = (props: any = {}) => { + return mount(UserAvatar, { + global: { + plugins: [PrimeVue, i18n], + components: { Avatar } + }, + props + }) + } + + it('renders correctly with photo Url', async () => { + const wrapper = mountComponent({ + photoUrl: 'https://example.com/avatar.jpg' + }) + + const avatar = wrapper.findComponent(Avatar) + expect(avatar.exists()).toBe(true) + expect(avatar.props('image')).toBe('https://example.com/avatar.jpg') + expect(avatar.props('icon')).toBeNull() + }) + + it('renders with default icon when no photo Url is provided', () => { + const wrapper = mountComponent({ + photoUrl: undefined + }) + + const avatar = wrapper.findComponent(Avatar) + expect(avatar.exists()).toBe(true) + expect(avatar.props('image')).toBeNull() + expect(avatar.props('icon')).toBe('pi pi-user') + }) + + it('renders with default icon when provided photo Url is null', () => { + const wrapper = mountComponent({ + photoUrl: null + }) + + const avatar = wrapper.findComponent(Avatar) + expect(avatar.exists()).toBe(true) + expect(avatar.props('image')).toBeNull() + expect(avatar.props('icon')).toBe('pi pi-user') + }) + + it('falls back to icon when image fails to load', async () => { + const wrapper = mountComponent({ + photoUrl: 'https://example.com/broken-image.jpg' + }) + + const avatar = wrapper.findComponent(Avatar) + expect(avatar.props('icon')).toBeNull() + + // Simulate image load error + avatar.vm.$emit('error') + await nextTick() + + expect(avatar.props('icon')).toBe('pi pi-user') + }) + + it('uses provided ariaLabel', () => { + const wrapper = mountComponent({ + photoUrl: 'https://example.com/avatar.jpg', + ariaLabel: 'Custom Label' + }) + + const avatar = wrapper.findComponent(Avatar) + expect(avatar.attributes('aria-label')).toBe('Custom Label') + }) + + it('falls back to i18n translation when no ariaLabel is provided', () => { + const wrapper = mountComponent({ + photoUrl: 'https://example.com/avatar.jpg' + }) + + const avatar = wrapper.findComponent(Avatar) + expect(avatar.attributes('aria-label')).toBe('User Avatar') + }) +}) diff --git a/src/components/common/UserAvatar.vue b/src/components/common/UserAvatar.vue new file mode 100644 index 000000000..8fce43d10 --- /dev/null +++ b/src/components/common/UserAvatar.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/components/dialog/content/setting/UserPanel.vue b/src/components/dialog/content/setting/UserPanel.vue index 2529261d7..9047d8c5a 100644 --- a/src/components/dialog/content/setting/UserPanel.vue +++ b/src/components/dialog/content/setting/UserPanel.vue @@ -5,12 +5,11 @@
-
@@ -87,13 +86,13 @@