Max 33274e5b9a
feat(webapp): user teaser popover (#8450)
* calculate distance between current user and queried user

* fix query for unset location

* use database to calculate distance

* rename distance to distance to me, 100% calculation done in DB

* distanceToMe tests

* lint fixes

* remove comments

* Show user teaser popover with badges, Desktop

* Refactor UserTeaser and add mobile popover support

* Avoid click propagation (WIP)

* Prevent event propagation

* Adjust alignment and font sizes

* More spacing for statistics

* Add distance, simplify user link

* Refactor location info into own component

* Add tests for UserTeaserPopup

* Refactor and test LocationInfo

* Query distanceToMe, rename distance to distanceToMe

* Update test

* Improve tests for UserTeaser, WIP

* Fix tests

* DistanceToMe on User instead of Location

* Revert "DistanceToMe on User instead of Location"

This reverts commit 96c9db00a44cd120e47bfe9534d3e066a194744c.

* Fix notifications

* Refactor UserTeaser and fix location info

* Fix group member crash

* Show 0 distance

* Fit in popover on small screens

* Allow access to profile on desktop

* Revert backend changes

* Load user teaser popover data only when needed

* Fix type mismatch

* Refactor for clarity and accessibility

* Litte refactorings and improvements

* Fix popover test

* Adapt and fix tests

* Fix tests and bugs

* Add placeholder

* cypress: adapt user teaser locator to changes

* Remove delays and scrolling

* Disable popovers in notification list and fix layout

* Remove flickering

* Make overlay catch all pointer events on touch devices

* Re-add attribute for E2E test

* Fix test, return to mouseover

* fix snapshot

---------

Co-authored-by: Ulf Gebhardt <ulf.gebhardt@webcraft-media.de>
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
Co-authored-by: mahula <lenzmath@posteo.de>
2025-05-05 23:54:13 +00:00

253 lines
6.5 KiB
JavaScript

import { render, screen, fireEvent } from '@testing-library/vue'
import { RouterLinkStub } from '@vue/test-utils'
import UserTeaser from './UserTeaser.vue'
import Vuex from 'vuex'
const localVue = global.localVue
// Mock Math.random, used in Dropdown
Object.assign(Math, {
random: () => 0,
})
const waitForPopover = async () => await new Promise((resolve) => setTimeout(resolve, 1000))
let mockIsTouchDevice
jest.mock('../utils/isTouchDevice', () => ({
isTouchDevice: jest.fn(() => mockIsTouchDevice),
}))
const userTilda = {
name: 'Tilda Swinton',
slug: 'tilda-swinton',
id: 'user1',
avatar: '/avatars/tilda-swinton',
badgeVerification: {
id: 'bv1',
icon: '/icons/verified',
description: 'Verified',
isDefault: false,
},
badgeTrophiesSelected: [
{
id: 'trophy1',
icon: '/icons/trophy1',
description: 'Trophy 1',
isDefault: false,
},
{
id: 'trophy2',
icon: '/icons/trophy2',
description: 'Trophy 2',
isDefault: false,
},
{
id: 'empty',
icon: '/icons/empty',
description: 'Empty',
isDefault: true,
},
],
}
describe('UserTeaser', () => {
const Wrapper = ({
isModerator = false,
withLinkToProfile = true,
onTouchScreen = false,
withAvatar = true,
user = userTilda,
withPopoverEnabled = true,
}) => {
mockIsTouchDevice = onTouchScreen
const store = new Vuex.Store({
getters: {
'auth/user': () => {
return {}
},
'auth/isModerator': () => isModerator,
},
})
return render(UserTeaser, {
localVue,
store,
propsData: {
user,
linkToProfile: withLinkToProfile,
showAvatar: withAvatar,
showPopover: withPopoverEnabled,
},
stubs: {
NuxtLink: RouterLinkStub,
'user-teaser-popover': true,
'v-popover': true,
'client-only': true,
},
mocks: {
$t: jest.fn((t) => t),
},
})
}
it('renders anonymous user', () => {
const wrapper = Wrapper({ user: null })
expect(wrapper.container).toMatchSnapshot()
})
describe('given an user', () => {
describe('without linkToProfile, on touch screen', () => {
let wrapper
beforeEach(() => {
wrapper = Wrapper({ withLinkToProfile: false, onTouchScreen: true })
})
it('renders', () => {
expect(wrapper.container).toMatchSnapshot()
})
describe('when clicking the user name', () => {
beforeEach(async () => {
const userName = screen.getByText('Tilda Swinton')
await fireEvent.click(userName)
await waitForPopover()
})
it('renders the popover', () => {
expect(wrapper.container).toMatchSnapshot()
})
})
describe('when clicking the user avatar', () => {
beforeEach(async () => {
const userAvatar = screen.getByAltText('Tilda Swinton')
await fireEvent.click(userAvatar)
await waitForPopover()
})
it('renders the popover', () => {
expect(wrapper.container).toMatchSnapshot()
})
})
})
describe('with linkToProfile, on touch screen', () => {
let wrapper
beforeEach(() => {
wrapper = Wrapper({ withLinkToProfile: true, onTouchScreen: true })
})
it('renders', () => {
expect(wrapper.container).toMatchSnapshot()
})
describe('when clicking the user name', () => {
beforeEach(async () => {
const userName = screen.getByText('Tilda Swinton')
await fireEvent.click(userName)
})
it('renders the popover', () => {
expect(wrapper.container).toMatchSnapshot()
})
})
})
describe('without linkToProfile, on desktop', () => {
let wrapper
beforeEach(() => {
wrapper = Wrapper({ withLinkToProfile: false, onTouchScreen: false })
})
it('renders', () => {
expect(wrapper.container).toMatchSnapshot()
})
describe('when hovering the user name', () => {
beforeEach(async () => {
const userName = screen.getByText('Tilda Swinton')
await fireEvent.mouseOver(userName)
await waitForPopover()
})
it('renders the popover', () => {
expect(wrapper.container).toMatchSnapshot()
})
})
describe('when hovering the user avatar', () => {
beforeEach(async () => {
const userAvatar = screen.getByAltText('Tilda Swinton')
await fireEvent.mouseOver(userAvatar)
await waitForPopover()
})
it('renders the popover', () => {
expect(wrapper.container).toMatchSnapshot()
})
})
})
describe('with linkToProfile, on desktop', () => {
let wrapper
beforeEach(() => {
wrapper = Wrapper({ withLinkToProfile: true, onTouchScreen: false })
})
it('renders', () => {
expect(wrapper.container).toMatchSnapshot()
})
describe('when hovering the user name', () => {
beforeEach(async () => {
const userName = screen.getByText('Tilda Swinton')
await fireEvent.mouseOver(userName)
await waitForPopover()
})
it('renders the popover', () => {
expect(wrapper.container).toMatchSnapshot()
})
})
})
describe('avatar is disabled', () => {
it('does not render the avatar', () => {
const wrapper = Wrapper({ withAvatar: false })
expect(wrapper.container).toMatchSnapshot()
})
})
describe('user is deleted', () => {
it('renders anonymous user', () => {
const wrapper = Wrapper({ user: { ...userTilda, deleted: true } })
expect(wrapper.container).toMatchSnapshot()
})
describe('even if the current user is a moderator', () => {
it('renders anonymous user', () => {
const wrapper = Wrapper({
user: { ...userTilda, deleted: true },
isModerator: true,
})
expect(wrapper.container).toMatchSnapshot()
})
})
})
describe('user is disabled', () => {
it('renders anonymous user', () => {
const wrapper = Wrapper({ user: { ...userTilda, disabled: true } })
expect(wrapper.container).toMatchSnapshot()
})
describe('current user is a moderator', () => {
it('renders user name', () => {
const wrapper = Wrapper({ user: { ...userTilda, disabled: true }, isModerator: true })
expect(wrapper.container).toMatchSnapshot()
})
})
})
})
})