import { fireEvent, render, waitFor } from '@testing-library/svelte' import { describe, expect, it, vi } from 'vitest' import { APP_SERVICES } from '$lib/app-services' import type { ApiClient } from '$lib/api/client' import type { Poll } from '$lib/api/types' import { account, session, testServices } from '$test/fixtures' import PollView from './PollView.svelte' function poll(overrides: Partial = {}): Poll { return { id: 'poll-1', expires_at: '2099-01-01T00:00:00.000Z', expired: false, multiple: false, votes_count: 0, options: [ { title: 'Cats', votes_count: null }, { title: 'Dogs', votes_count: null }, { title: 'Both', votes_count: null }, ], emojis: [], voted: false, own_votes: [], ...overrides, } } function signedServices( votePoll: (api: ApiClient, id: string, choices: number[]) => Promise, ) { return testServices({ session: session({ token: 'token', me: account(), signedIn: true }), endpoints: { votePoll }, }) } describe('PollView', () => { it('submits one selected index and immediately shows returned results', async () => { const updated = poll({ voted: true, votes_count: 3, own_votes: [1], options: [ { title: 'Cats', votes_count: 1 }, { title: 'Dogs', votes_count: 2 }, { title: 'Both', votes_count: 0 }, ], }) const votePoll = vi.fn().mockResolvedValue(updated) const onupdate = vi.fn() const view = render(PollView, { props: { poll: poll(), authorId: 'someone-else', onupdate }, context: new Map([[APP_SERVICES, signedServices(votePoll)]]), }) await fireEvent.click(view.getByRole('radio', { name: 'Dogs' })) await fireEvent.click(view.getByRole('button', { name: 'Vote' })) await waitFor(() => expect(votePoll).toHaveBeenCalledOnce()) expect(votePoll.mock.calls[0][1]).toBe('poll-1') expect(votePoll.mock.calls[0][2]).toEqual([1]) expect(onupdate).toHaveBeenCalledWith(updated) expect(await view.findByText('67%')).toBeInTheDocument() expect(view.getByLabelText('Your vote')).toBeInTheDocument() }) it('submits every selected index for a multiple-choice poll', async () => { const votePoll = vi.fn().mockResolvedValue( poll({ multiple: true, voted: true, own_votes: [0, 2] }), ) const view = render(PollView, { props: { poll: poll({ multiple: true }), authorId: 'someone-else' }, context: new Map([[APP_SERVICES, signedServices(votePoll)]]), }) await fireEvent.click(view.getByRole('checkbox', { name: 'Cats' })) await fireEvent.click(view.getByRole('checkbox', { name: 'Both' })) await fireEvent.click(view.getByRole('button', { name: 'Vote' })) await waitFor(() => expect(votePoll).toHaveBeenCalledOnce()) expect(votePoll.mock.calls[0][2]).toEqual([0, 2]) }) it('shows open poll options to signed-out readers without enabling a vote', () => { const view = render(PollView, { props: { poll: poll(), authorId: 'someone-else' }, context: new Map([[APP_SERVICES, testServices()]]), }) expect(view.getByRole('radio', { name: 'Cats' })).toBeDisabled() expect(view.getByRole('link', { name: 'Sign in to vote' })).toHaveAttribute('href', '#/login') expect(view.queryByRole('button', { name: 'Vote' })).not.toBeInTheDocument() }) it('renders custom emoji in poll choices', () => { const view = render(PollView, { props: { poll: poll({ options: [ { title: 'Choose :blobcat:', votes_count: null }, { title: 'No thanks', votes_count: null }, ], emojis: [ { shortcode: 'blobcat', url: 'https://cdn.example/blobcat.png', static_url: 'https://cdn.example/blobcat.png', visible_in_picker: true, }, ], }), authorId: 'someone-else', }, context: new Map([[APP_SERVICES, testServices()]]), }) expect(view.getByAltText(':blobcat:')).toHaveAttribute( 'src', 'https://cdn.example/blobcat.png', ) expect(view.getByRole('radio', { name: 'Choose :blobcat:' })).toBeDisabled() }) })