sebastian2357 2fd138697f
feat(webapp): badges UI (#8426)
- New badge UI, including editor.
- Adds config to enable/disable badges.

---------

Co-authored-by: Sebastian Stein <sebastian@codepassion.de>
Co-authored-by: Maximilian Harz <maxharz@gmail.com>
2025-04-25 16:55:46 +00:00

303 lines
8.3 KiB
JavaScript

import { render, screen, fireEvent } from '@testing-library/vue'
import '@testing-library/jest-dom'
import badges from './badges.vue'
const localVue = global.localVue
describe('badge settings', () => {
let mocks
const apolloMutateMock = jest.fn()
const Wrapper = () => {
return render(badges, {
localVue,
mocks,
})
}
beforeEach(() => {
mocks = {
$t: jest.fn((t) => t),
$toast: {
success: jest.fn(),
error: jest.fn(),
},
$apollo: {
mutate: apolloMutateMock,
},
}
})
describe('without badges', () => {
beforeEach(() => {
mocks.$store = {
getters: {
'auth/isModerator': () => false,
'auth/user': {
id: 'u23',
badgeVerification: {
id: 'bv1',
icon: '/verification/icon',
description: 'Verification description',
isDefault: true,
},
badgeTrophiesSelected: [],
badgeTrophiesUnused: [],
},
},
}
})
it('renders', () => {
const wrapper = Wrapper()
expect(wrapper.container).toMatchSnapshot()
})
})
describe('with badges', () => {
const badgeTrophiesSelected = [
{
id: '1',
icon: '/path/to/some/icon',
isDefault: false,
description: 'Some description',
},
{
id: '2',
icon: '/path/to/empty/icon',
isDefault: true,
description: 'Empty',
},
{
id: '3',
icon: '/path/to/third/icon',
isDefault: false,
description: 'Third description',
},
]
const badgeTrophiesUnused = [
{
id: '4',
icon: '/path/to/fourth/icon',
description: 'Fourth description',
},
{
id: '5',
icon: '/path/to/fifth/icon',
description: 'Fifth description',
},
]
let wrapper
beforeEach(() => {
mocks.$store = {
getters: {
'auth/isModerator': () => false,
'auth/user': {
id: 'u23',
badgeVerification: {
id: 'bv1',
icon: '/verification/icon',
description: 'Verification description',
isDefault: false,
},
badgeTrophiesSelected,
badgeTrophiesUnused,
},
},
}
wrapper = Wrapper()
})
it('renders', () => {
expect(wrapper.container).toMatchSnapshot()
})
describe('selecting a used badge', () => {
beforeEach(async () => {
const badge = screen.getByTitle(badgeTrophiesSelected[0].description)
await fireEvent.click(badge)
})
it('shows remove badge button', () => {
expect(screen.getByText('settings.badges.remove')).toBeInTheDocument()
})
describe('clicking remove badge button', () => {
const clickButton = async () => {
const removeButton = screen.getByText('settings.badges.remove')
await fireEvent.click(removeButton)
}
describe('with successful server request', () => {
beforeEach(() => {
apolloMutateMock.mockResolvedValue({
data: {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: [
{
id: '2',
icon: '/path/to/empty/icon',
isDefault: true,
description: 'Empty',
},
{
id: '2',
icon: '/path/to/empty/icon',
isDefault: true,
description: 'Empty',
},
{
id: '3',
icon: '/path/to/third/icon',
isDefault: false,
description: 'Third description',
},
],
},
},
})
clickButton()
})
it('calls the server', () => {
expect(apolloMutateMock).toHaveBeenCalledWith({
mutation: expect.anything(),
update: expect.anything(),
variables: {
badgeId: null,
slot: 0,
},
})
})
/* To test this, we would need a better apollo mock */
it.skip('removes the badge', async () => {
expect(wrapper.container).toMatchSnapshot()
})
it('shows a success message', () => {
expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update')
})
})
describe('with failed server request', () => {
beforeEach(() => {
apolloMutateMock.mockRejectedValue({ message: 'Ouch!' })
clickButton()
})
it('shows an error message', () => {
expect(mocks.$toast.error).toHaveBeenCalledWith('settings.badges.error-update')
})
})
})
})
describe('no more badges available', () => {
beforeEach(async () => {
mocks.$store.getters['auth/user'].badgeTrophiesUnused = []
})
describe('selecting an empty slot', () => {
beforeEach(async () => {
const emptySlot = screen.getAllByTitle('Empty')[0]
await fireEvent.click(emptySlot)
})
it('shows no more badges available message', () => {
expect(wrapper.container).toMatchSnapshot()
})
})
})
describe('more badges available', () => {
describe('selecting an empty slot', () => {
beforeEach(async () => {
const emptySlot = screen.getAllByTitle('Empty')[0]
await fireEvent.click(emptySlot)
})
it('shows list with available badges', () => {
expect(wrapper.container).toMatchSnapshot()
})
describe('clicking on an available badge', () => {
const clickBadge = async () => {
const badge = screen.getByText(badgeTrophiesUnused[0].description)
await fireEvent.click(badge)
}
describe('with successful server request', () => {
beforeEach(() => {
apolloMutateMock.mockResolvedValue({
data: {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: [
{
id: '4',
icon: '/path/to/fourth/icon',
description: 'Fourth description',
isDefault: false,
},
{
id: '2',
icon: '/path/to/empty/icon',
isDefault: true,
description: 'Empty',
},
{
id: '3',
icon: '/path/to/third/icon',
isDefault: false,
description: 'Third description',
},
],
},
},
})
clickBadge()
})
it('calls the server', () => {
expect(apolloMutateMock).toHaveBeenCalledWith({
mutation: expect.anything(),
update: expect.anything(),
variables: {
badgeId: '4',
slot: 1,
},
})
})
/* To test this, we would need a better apollo mock */
it.skip('adds the badge', async () => {
expect(wrapper.container).toMatchSnapshot()
})
it('shows a success message', () => {
expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update')
})
})
describe('with failed server request', () => {
beforeEach(() => {
apolloMutateMock.mockRejectedValue({ message: 'Ouch!' })
clickBadge()
})
it('shows an error message', () => {
expect(mocks.$toast.error).toHaveBeenCalledWith('settings.badges.error-update')
})
})
})
})
})
})
})