593 lines
18 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(() => {
apolloMutateMock.mockReset()
mocks = {
$t: jest.fn((t) => t),
$toast: {
success: jest.fn(),
error: jest.fn(),
info: 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 = {
commit: jest.fn(),
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', () => {
const removedResponseData = {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: [
{
id: 'empty-0',
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',
},
],
badgeTrophiesUnused: [
{
id: '1',
icon: '/path/to/some/icon',
description: 'Some description',
},
{
id: '4',
icon: '/path/to/fourth/icon',
description: 'Fourth description',
},
{
id: '5',
icon: '/path/to/fifth/icon',
description: 'Fifth description',
},
],
},
}
beforeEach(async () => {
apolloMutateMock.mockImplementation(({ update }) => {
const result = { data: removedResponseData }
if (update) update(null, result)
return Promise.resolve(result)
})
await clickButton()
})
it('calls the server', () => {
expect(apolloMutateMock).toHaveBeenCalledWith({
mutation: expect.anything(),
update: expect.anything(),
variables: {
badgeId: null,
slot: 0,
},
})
})
it('updates badges in store via update callback', () => {
expect(mocks.$store.commit).toHaveBeenCalledWith(
'auth/SET_USER',
expect.objectContaining({
id: 'u23',
badgeTrophiesSelected:
removedResponseData.setTrophyBadgeSelected.badgeTrophiesSelected,
badgeTrophiesUnused: removedResponseData.setTrophyBadgeSelected.badgeTrophiesUnused,
}),
)
})
it('shows a success message', () => {
expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update')
})
})
describe('with failed server request', () => {
beforeEach(async () => {
apolloMutateMock.mockRejectedValue({ message: 'Ouch!' })
await 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', () => {
const addedResponseData = {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: [
{
id: '1',
icon: '/path/to/some/icon',
isDefault: false,
description: 'Some description',
},
{
id: '4',
icon: '/path/to/fourth/icon',
isDefault: false,
description: 'Fourth description',
},
{
id: '3',
icon: '/path/to/third/icon',
isDefault: false,
description: 'Third description',
},
],
},
}
beforeEach(async () => {
apolloMutateMock.mockImplementation(({ update }) => {
const result = { data: addedResponseData }
if (update) update(null, result)
return Promise.resolve(result)
})
await clickBadge()
})
it('calls the server', () => {
expect(apolloMutateMock).toHaveBeenCalledWith({
mutation: expect.anything(),
update: expect.anything(),
variables: {
badgeId: '4',
slot: 1,
},
})
})
it('updates badges in store via update callback', () => {
expect(mocks.$store.commit).toHaveBeenCalledWith(
'auth/SET_USER',
expect.objectContaining({
id: 'u23',
badgeTrophiesSelected:
addedResponseData.setTrophyBadgeSelected.badgeTrophiesSelected,
}),
)
})
it('shows a success message', () => {
expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update')
})
})
describe('with failed server request', () => {
beforeEach(async () => {
apolloMutateMock.mockRejectedValue({ message: 'Ouch!' })
await clickBadge()
})
it('shows an error message', () => {
expect(mocks.$toast.error).toHaveBeenCalledWith('settings.badges.error-update')
})
})
})
})
})
describe('drag and drop', () => {
const makeDropData = (source) => JSON.stringify(source)
describe('assign badge from reserve to empty slot via DnD', () => {
const assignResponseData = {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: [
badgeTrophiesSelected[0],
{
id: '4',
icon: '/path/to/fourth/icon',
isDefault: false,
description: 'Fourth description',
},
badgeTrophiesSelected[2],
],
badgeTrophiesUnused: [badgeTrophiesUnused[1]],
},
}
beforeEach(async () => {
apolloMutateMock.mockImplementation(({ update }) => {
const result = { data: assignResponseData }
if (update) update(null, result)
return Promise.resolve(result)
})
// Simulate dropping a reserve badge on the empty hex slot (index 2 in Badges = slot 1)
const emptySlot = screen.getAllByTitle('Empty')[0]
const container = emptySlot.closest('.hc-badge-container')
const sourceData = makeDropData({
source: 'reserve',
badge: badgeTrophiesUnused[0],
})
await fireEvent.drop(container, {
dataTransfer: { getData: () => sourceData },
})
})
it('calls the server with correct badge and slot', () => {
expect(apolloMutateMock).toHaveBeenCalledWith(
expect.objectContaining({
variables: {
badgeId: '4',
slot: 1,
},
}),
)
})
it('shows success message', () => {
expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update')
})
})
describe('remove badge from hex to reserve via DnD', () => {
const removeResponseData = {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: [
{
id: 'empty-0',
icon: '/path/to/empty/icon',
isDefault: true,
description: 'Empty',
},
badgeTrophiesSelected[1],
badgeTrophiesSelected[2],
],
badgeTrophiesUnused: [badgeTrophiesSelected[0], ...badgeTrophiesUnused],
},
}
beforeEach(async () => {
apolloMutateMock.mockImplementation(({ update }) => {
const result = { data: removeResponseData }
if (update) update(null, result)
return Promise.resolve(result)
})
// Simulate dropping a hex badge on the reserve container
const reserveContainer = wrapper.container.querySelector('.badge-selection')
const hexData = makeDropData({
source: 'hex',
index: 1,
badge: badgeTrophiesSelected[0],
})
await fireEvent.drop(reserveContainer, {
dataTransfer: { getData: () => hexData },
})
})
it('calls the server to remove badge', () => {
expect(apolloMutateMock).toHaveBeenCalledWith(
expect.objectContaining({
variables: {
badgeId: null,
slot: 0,
},
}),
)
})
it('shows success message', () => {
expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update')
})
})
describe('swap two badges via DnD', () => {
const swapResponse1 = {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: [
{
id: '3',
icon: '/path/to/third/icon',
isDefault: false,
description: 'Third description',
},
badgeTrophiesSelected[1],
{
id: 'empty-temp',
icon: '/path/to/empty/icon',
isDefault: true,
description: 'Empty',
},
],
badgeTrophiesUnused: [badgeTrophiesSelected[0], ...badgeTrophiesUnused],
},
}
const swapResponse2 = {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: [
{
id: '3',
icon: '/path/to/third/icon',
isDefault: false,
description: 'Third description',
},
badgeTrophiesSelected[1],
{
id: '1',
icon: '/path/to/some/icon',
isDefault: false,
description: 'Some description',
},
],
badgeTrophiesUnused,
},
}
beforeEach(async () => {
let callCount = 0
apolloMutateMock.mockImplementation(({ update }) => {
callCount++
const responseData = callCount === 1 ? swapResponse1 : swapResponse2
const result = { data: responseData }
if (update) update(null, result)
return Promise.resolve(result)
})
// Simulate dragging badge at index 1 onto badge at index 3 (both occupied)
const targetBadge = screen.getByTitle(badgeTrophiesSelected[2].description)
const container = targetBadge.closest('.hc-badge-container')
const sourceData = makeDropData({
source: 'hex',
index: 1,
badge: badgeTrophiesSelected[0],
})
await fireEvent.drop(container, {
dataTransfer: { getData: () => sourceData },
})
})
it('calls the server twice for swap', () => {
expect(apolloMutateMock).toHaveBeenCalledTimes(2)
})
it('first mutation moves source to target slot', () => {
expect(apolloMutateMock).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
variables: {
badgeId: '1',
slot: 2,
},
}),
)
})
it('second mutation moves former target to source slot', () => {
expect(apolloMutateMock).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
variables: {
badgeId: '3',
slot: 0,
},
}),
)
})
it('shows success message', () => {
expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update')
})
})
describe('swap with partial failure', () => {
beforeEach(async () => {
let callCount = 0
apolloMutateMock.mockImplementation(({ update }) => {
callCount++
if (callCount === 1) {
const result = {
data: {
setTrophyBadgeSelected: {
id: 'u23',
badgeTrophiesSelected: badgeTrophiesSelected,
badgeTrophiesUnused: badgeTrophiesUnused,
},
},
}
if (update) update(null, result)
return Promise.resolve(result)
}
return Promise.reject(new Error('Server error'))
})
const targetBadge = screen.getByTitle(badgeTrophiesSelected[2].description)
const container = targetBadge.closest('.hc-badge-container')
const sourceData = makeDropData({
source: 'hex',
index: 1,
badge: badgeTrophiesSelected[0],
})
await fireEvent.drop(container, {
dataTransfer: { getData: () => sourceData },
})
})
it('shows swap partial error', () => {
expect(mocks.$toast.error).toHaveBeenCalledWith('settings.badges.swap-partial-error')
})
})
})
})
})