Files
plspace/src/routes/Profile.test.ts
T

237 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { act, render, waitFor } from '@testing-library/svelte'
import { describe, expect, it, vi } from 'vitest'
import { APP_SERVICES } from '$lib/app-services'
import type { Account } from '$lib/api/types'
import { TIMELINE_REFRESH, TimelineRefreshController } from '$lib/timeline-refresh'
import { account, deferred, session, status, testServices, theme } from '$test/fixtures'
import Profile from './Profile.svelte'
describe('Profile', () => {
it('renders account custom emoji in the public profile heading', async () => {
const profileAccount = account({
display_name: ':smugkura: Kura :disconnecting: :kura_explode:',
emojis: [
{
shortcode: 'smugkura',
url: 'https://cdn.example/smugkura.png',
static_url: 'https://cdn.example/smugkura.png',
visible_in_picker: true,
},
{
shortcode: 'disconnecting',
url: 'https://cdn.example/disconnecting.png',
static_url: 'https://cdn.example/disconnecting.png',
visible_in_picker: true,
},
{
shortcode: 'kura_explode',
url: 'https://cdn.example/kura_explode.png',
static_url: 'https://cdn.example/kura_explode.png',
visible_in_picker: true,
},
],
})
const services = testServices({
session: session(),
theme: theme(),
endpoints: {
lookupAccount: vi.fn().mockResolvedValue(profileAccount),
fetchAccountStatuses: vi.fn().mockResolvedValue({ items: [], links: {} }),
},
})
const view = render(Profile, {
props: { acct: 'alice' },
context: new Map([[APP_SERVICES, services]]),
})
const headingEmoji = (await view.findAllByAltText(':smugkura:')).find((node) =>
node.closest('h1.profile-name'),
)
expect(headingEmoji).toBeDefined()
expect(view.getAllByAltText(':disconnecting:').length).toBeGreaterThan(0)
expect(view.getAllByAltText(':kura_explode:').length).toBeGreaterThan(0)
})
it('does not apply profile CSS after it has unmounted', async () => {
const lookup = deferred<Account>()
const applyProfileCss = vi.fn()
const services = testServices({
session: session(),
theme: theme({ applyProfileCss }),
endpoints: { lookupAccount: vi.fn(() => lookup.promise) },
})
const view = render(Profile, {
props: { acct: 'alice' },
context: new Map([[APP_SERVICES, services]]),
})
view.unmount()
await act(() => lookup.resolve(account({ fields: [{ name: 'css', value: '.x { color: red }' }] })))
expect(applyProfileCss).not.toHaveBeenCalled()
})
it('shows only original top-level notes in Latest Blog Entries', async () => {
const original = status({
id: 'original',
content: '<p>Original note</p>',
})
const boostedNote = status({
id: 'boost-wrapper',
content: '',
reblog: status({ id: 'boosted', content: '<p>Someone elses note</p>' }),
})
const reply = status({
id: 'reply',
content: '<p>A reply</p>',
in_reply_to_id: 'parent',
})
const fetchAccountStatuses = vi.fn().mockResolvedValue({
items: [original, boostedNote, reply],
links: {},
})
const services = testServices({
session: session(),
theme: theme(),
endpoints: {
lookupAccount: vi.fn().mockResolvedValue(account()),
fetchAccountStatuses,
},
})
const view = render(Profile, {
props: { acct: 'alice', view: 'profile' },
context: new Map([[APP_SERVICES, services]]),
})
await waitFor(() => expect(fetchAccountStatuses).toHaveBeenCalledOnce())
expect(fetchAccountStatuses.mock.calls[0][3]).toEqual({
exclude_replies: true,
exclude_reblogs: true,
})
expect(await view.findByText('Original note')).toBeInTheDocument()
expect(view.queryByText('Someone elses note')).not.toBeInTheDocument()
expect(view.queryByText('A reply')).not.toBeInTheDocument()
})
it('builds Pics from the accounts own image attachments', async () => {
const ownPicture = status({
id: 'picture-entry',
content: '<p>A day at the beach</p>',
media_attachments: [
{
id: 'picture',
type: 'image',
url: 'https://media.example/beach.jpg',
preview_url: 'https://media.example/beach-small.jpg',
description: 'Sunset over the beach',
},
],
})
const video = status({
id: 'video-entry',
content: '<p>A video that does not belong in Pics</p>',
media_attachments: [
{
id: 'video',
type: 'video',
url: 'https://media.example/movie.mp4',
preview_url: 'https://media.example/movie.jpg',
description: 'A movie',
},
],
})
const repostedPicture = status({
id: 'repost',
reblog: status({
id: 'someone-elses-picture',
content: '<p>Someone elses picture</p>',
media_attachments: [
{
id: 'reposted-picture',
type: 'image',
url: 'https://media.example/reposted.jpg',
preview_url: null,
description: 'A reposted picture',
},
],
}),
})
const fetchAccountStatuses = vi.fn().mockResolvedValue({
items: [ownPicture, video, repostedPicture],
links: {},
})
const services = testServices({
session: session(),
theme: theme(),
endpoints: {
lookupAccount: vi.fn().mockResolvedValue(account()),
fetchAccountStatuses,
},
})
const view = render(Profile, {
props: { acct: 'alice', view: 'pics' },
context: new Map([[APP_SERVICES, services]]),
})
expect(await view.findByRole('img', { name: 'Sunset over the beach' })).toHaveAttribute(
'src',
'https://media.example/beach-small.jpg',
)
expect(fetchAccountStatuses.mock.calls[0][3]).toEqual({
only_media: true,
exclude_reblogs: true,
})
expect(view.getByText('Sunset over the beach')).toBeInTheDocument()
expect(view.queryByText('A movie')).not.toBeInTheDocument()
expect(view.queryByText('A reposted picture')).not.toBeInTheDocument()
})
it('refreshes only the entry feed without reloading the profile', async () => {
const lookupAccount = vi.fn().mockResolvedValue(account())
const fetchAccountStatuses = vi
.fn()
.mockResolvedValueOnce({
items: [
status({
id: 'timeline-note',
content: '<p>Timeline note</p>',
favourites_count: 0,
}),
],
links: {},
})
.mockResolvedValueOnce({
items: [
status({
id: 'timeline-note',
content: '<p>Timeline note</p>',
favourites_count: 5,
}),
],
links: {},
})
const services = testServices({
session: session(),
theme: theme(),
endpoints: { lookupAccount, fetchAccountStatuses },
})
const refresh = new TimelineRefreshController()
const context = new Map()
context.set(APP_SERVICES, services)
context.set(TIMELINE_REFRESH, refresh)
const view = render(Profile, {
props: { acct: 'alice', view: 'blog' },
context,
})
expect(await view.findByText('Timeline note')).toBeInTheDocument()
expect(refresh.refresh()).toBe(true)
await waitFor(() => expect(fetchAccountStatuses).toHaveBeenCalledTimes(2))
expect(lookupAccount).toHaveBeenCalledOnce()
await waitFor(() =>
expect(view.getByRole('button', { name: 'Kudos (5)' })).toBeInTheDocument(),
)
})
})