import { describe, expect, it } from 'vitest'; import { applyPollResult } from '../src/services/nowPlaying'; import { bearer, createUser, follow, useTestApp } from './helpers'; const app = useTestApp(); const song = (title: string) => ({ track: { title, artist: 'Massive Attack' }, isPlaying: true, progressMs: null }); async function setup() { const alice = await createUser(app(), 'alice'); const bob = await createUser(app(), 'bob'); const carol = await createUser(app(), 'carol'); await follow(app(), bob.token, 'alice'); await applyPollResult(alice.user.id, 'lastfm', song('Teardrop')); const feed = (await app().inject({ url: '/feed', headers: bearer(bob.token) })).json(); return { alice, bob, carol, listenId: feed[0].listenId as string }; } const react = (token: string, listenId: string, emoji: string) => app().inject({ method: 'PUT', url: `/listens/${listenId}/reaction`, headers: bearer(token), payload: { emoji } }); describe('reactions', () => { it('shows up on the feed and profile, grouped by emoji', async () => { const { alice, bob, carol, listenId } = await setup(); expect(listenId).toBeTruthy(); await react(bob.token, listenId, '🔥'); const res = await react(carol.token, listenId, '🔥'); expect(res.json()).toEqual([{ emoji: '🔥', count: 2, users: [expect.objectContaining({ username: 'carol' }), expect.objectContaining({ username: 'bob' })] }]); const profile = (await app().inject({ url: '/users/alice', headers: bearer(alice.token) })).json(); expect(profile.nowPlaying.reactions[0]).toMatchObject({ emoji: '🔥', count: 2 }); }); it('one per person: a new emoji replaces the old one, and it can be removed', async () => { const { bob, listenId } = await setup(); await react(bob.token, listenId, '🔥'); expect((await react(bob.token, listenId, '😂')).json()).toEqual([expect.objectContaining({ emoji: '😂', count: 1 })]); const removed = await app().inject({ method: 'DELETE', url: `/listens/${listenId}/reaction`, headers: bearer(bob.token) }); expect(removed.json()).toEqual([]); }); it('follows the play into recently played', async () => { const { alice, bob, listenId } = await setup(); await react(bob.token, listenId, '❤️'); await applyPollResult(alice.user.id, 'lastfm', song('Angel')); const profile = (await app().inject({ url: '/users/alice', headers: bearer(bob.token) })).json(); expect(profile.nowPlaying.reactions).toEqual([]); expect(profile.recent[0]).toMatchObject({ listenId, reactions: [expect.objectContaining({ emoji: '❤️' })] }); }); it('rejects anything but one emoji, your own plays, and blocked people', async () => { const { alice, bob, listenId } = await setup(); expect((await react(bob.token, listenId, 'lol')).statusCode).toBe(400); expect((await react(bob.token, listenId, '🔥🔥')).statusCode).toBe(400); expect((await react(alice.token, listenId, '🔥')).statusCode).toBe(400); await react(bob.token, listenId, '🔥'); await app().inject({ method: 'POST', url: '/users/bob/block', headers: bearer(alice.token), payload: {} }); expect((await react(bob.token, listenId, '😂')).statusCode).toBe(404); const profile = (await app().inject({ url: '/users/alice', headers: bearer(alice.token) })).json(); expect(profile.nowPlaying.reactions).toEqual([]); // their earlier reaction went with the block }); });