mirror of
https://git.shipoclu.com/moon/plspace.git
synced 2026-08-13 02:42:30 +00:00
261 lines
8.4 KiB
TypeScript
261 lines
8.4 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { ApiClient } from './client'
|
|
import {
|
|
fetchNotifications,
|
|
fetchQuotes,
|
|
postStatus,
|
|
setEmojiReaction,
|
|
updateProfileFields,
|
|
updatePublicProfile,
|
|
votePoll,
|
|
} from './endpoints'
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals()
|
|
})
|
|
|
|
describe('updateProfileFields', () => {
|
|
it('uses the Pleroma/Mastodon indexed multipart representation', async () => {
|
|
const fetchMock = vi.fn(async (_url: string, init?: RequestInit) => {
|
|
expect(init?.method).toBe('PATCH')
|
|
expect(init?.headers).toBeInstanceOf(Headers)
|
|
expect((init?.headers as Headers).get('Authorization')).toBe('Bearer token')
|
|
expect(init?.body).toBeInstanceOf(FormData)
|
|
const form = init?.body as FormData
|
|
expect([...form.entries()]).toEqual([
|
|
['fields_attributes[0][name]', 'Website'],
|
|
['fields_attributes[0][value]', 'https://example.test'],
|
|
['fields_attributes[1][name]', 'plspace:css1'],
|
|
['fields_attributes[1][value]', 'body { color: pink; }'],
|
|
])
|
|
return new Response(JSON.stringify({ id: 'account-1', fields: [] }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
})
|
|
vi.stubGlobal('fetch', fetchMock)
|
|
|
|
await updateProfileFields(new ApiClient('example.test', 'token'), [
|
|
{ name: 'Website', value: 'https://example.test' },
|
|
{ name: 'plspace:css1', value: 'body { color: pink; }' },
|
|
])
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
'https://example.test/api/v1/accounts/update_credentials',
|
|
expect.any(Object),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('updatePublicProfile', () => {
|
|
it('sends public text, images, fields and Pleroma identity data as multipart form data', async () => {
|
|
const avatar = new File(['avatar'], 'avatar.png', { type: 'image/png' })
|
|
const fetchMock = vi.fn(async (_url: string, init?: RequestInit) => {
|
|
const form = init?.body as FormData
|
|
expect(init?.method).toBe('PATCH')
|
|
expect(form.get('display_name')).toBe('Alice Example')
|
|
expect(form.get('note')).toBe('My public bio')
|
|
expect(form.get('avatar')).toBe(avatar)
|
|
expect(form.get('header')).toBe('')
|
|
expect(form.get('actor_type')).toBe('Group')
|
|
expect(form.get('birthday')).toBe('2000-01-02')
|
|
expect(form.get('show_birthday')).toBe('true')
|
|
expect(form.get('fields_attributes[0][name]')).toBe('Website')
|
|
expect(form.get('fields_attributes[0][value]')).toBe('https://example.test')
|
|
return new Response(JSON.stringify({ id: 'account-1', fields: [] }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
})
|
|
vi.stubGlobal('fetch', fetchMock)
|
|
|
|
await updatePublicProfile(new ApiClient('example.test', 'token'), {
|
|
displayName: 'Alice Example',
|
|
note: 'My public bio',
|
|
avatar,
|
|
header: '',
|
|
actorType: 'Group',
|
|
birthday: '2000-01-02',
|
|
showBirthday: true,
|
|
fields: [{ name: 'Website', value: 'https://example.test' }],
|
|
})
|
|
})
|
|
|
|
it('sends an empty sentinel row when all public fields are cleared', async () => {
|
|
const fetchMock = vi.fn(async (_url: string, init?: RequestInit) => {
|
|
const form = init?.body as FormData
|
|
expect(form.get('fields_attributes[0][name]')).toBe('')
|
|
expect(form.get('fields_attributes[0][value]')).toBe('')
|
|
return new Response(JSON.stringify({ id: 'account-1', fields: [] }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
})
|
|
vi.stubGlobal('fetch', fetchMock)
|
|
|
|
await updatePublicProfile(new ApiClient('example.test', 'token'), { fields: [] })
|
|
})
|
|
})
|
|
|
|
describe('poll endpoints', () => {
|
|
it('submits selected poll option indexes as JSON', async () => {
|
|
const fetchMock = vi.fn(async (_url: string, init?: RequestInit) => {
|
|
expect(init?.method).toBe('POST')
|
|
expect(JSON.parse(String(init?.body))).toEqual({ choices: [0, 2] })
|
|
return new Response(
|
|
JSON.stringify({
|
|
id: 'poll-1',
|
|
expired: false,
|
|
multiple: true,
|
|
votes_count: 2,
|
|
options: [],
|
|
emojis: [],
|
|
voted: true,
|
|
own_votes: [0, 2],
|
|
}),
|
|
{ status: 200, headers: { 'Content-Type': 'application/json' } },
|
|
)
|
|
})
|
|
vi.stubGlobal('fetch', fetchMock)
|
|
|
|
await votePoll(new ApiClient('example.test', 'token'), 'poll/one', [0, 2])
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
'https://example.test/api/v1/polls/poll%2Fone/votes',
|
|
expect.any(Object),
|
|
)
|
|
})
|
|
|
|
it('includes a standard Mastodon/Pleroma poll in a new status', async () => {
|
|
const fetchMock = vi.fn(async (_url: string, init?: RequestInit) => {
|
|
expect(JSON.parse(String(init?.body))).toMatchObject({
|
|
status: 'Pick a snack',
|
|
poll: {
|
|
options: ['Cake', 'Fruit'],
|
|
expires_in: 3600,
|
|
multiple: false,
|
|
},
|
|
})
|
|
return new Response(JSON.stringify({ id: 'status-1' }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
})
|
|
vi.stubGlobal('fetch', fetchMock)
|
|
|
|
await postStatus(new ApiClient('example.test', 'token'), {
|
|
status: 'Pick a snack',
|
|
poll: {
|
|
options: ['Cake', 'Fruit'],
|
|
expires_in: 3600,
|
|
multiple: false,
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('emoji reaction endpoints', () => {
|
|
it.each([
|
|
[true, 'PUT'],
|
|
[false, 'DELETE'],
|
|
] as const)('uses the Pleroma/Akkoma reaction endpoint (on=%s)', async (on, method) => {
|
|
const fetchMock = vi.fn(async (_url: string, init?: RequestInit) => {
|
|
expect(init?.method).toBe(method)
|
|
return new Response(JSON.stringify({ id: 'status/one' }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
})
|
|
vi.stubGlobal('fetch', fetchMock)
|
|
|
|
await setEmojiReaction(
|
|
new ApiClient('example.test', 'token'),
|
|
'status/one',
|
|
'party_blob@remote.example',
|
|
on,
|
|
)
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
'https://example.test/api/v1/pleroma/statuses/status%2Fone/reactions/party_blob%40remote.example',
|
|
expect.any(Object),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('quote endpoints', () => {
|
|
it('sends both current and legacy quote parameters when composing', async () => {
|
|
const fetchMock = vi.fn(async (_url: string, init?: RequestInit) => {
|
|
expect(JSON.parse(String(init?.body))).toMatchObject({
|
|
status: 'Commentary',
|
|
quoted_status_id: 'quoted/one',
|
|
quote_id: 'quoted/one',
|
|
})
|
|
return new Response(JSON.stringify({ id: 'quote-1' }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
})
|
|
vi.stubGlobal('fetch', fetchMock)
|
|
|
|
await postStatus(new ApiClient('example.test', 'token'), {
|
|
status: 'Commentary',
|
|
quoted_status_id: 'quoted/one',
|
|
})
|
|
})
|
|
|
|
it('falls back to the legacy Pleroma quote-list endpoint', async () => {
|
|
const fetchMock = vi
|
|
.fn()
|
|
.mockResolvedValueOnce(
|
|
new Response(JSON.stringify({ error: 'Not found' }), {
|
|
status: 404,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
)
|
|
.mockResolvedValueOnce(
|
|
new Response(JSON.stringify([{ id: 'quote-1' }]), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
)
|
|
vi.stubGlobal('fetch', fetchMock)
|
|
|
|
const page = await fetchQuotes(new ApiClient('old-pleroma.example', 'token'), 'status/one', {
|
|
limit: 20,
|
|
})
|
|
|
|
expect(page.items).toEqual([{ id: 'quote-1' }])
|
|
expect(fetchMock.mock.calls[0][0]).toBe(
|
|
'https://old-pleroma.example/api/v1/statuses/status%2Fone/quotes?limit=20',
|
|
)
|
|
expect(fetchMock.mock.calls[1][0]).toBe(
|
|
'https://old-pleroma.example/api/v1/pleroma/statuses/status%2Fone/quotes?limit=20',
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('fetchNotifications', () => {
|
|
it('defensively applies requested types when a server ignores the filter', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn().mockResolvedValue(
|
|
new Response(
|
|
JSON.stringify([
|
|
{ id: 'favourite-1', type: 'favourite' },
|
|
{ id: 'mention-1', type: 'mention' },
|
|
]),
|
|
{ status: 200, headers: { 'Content-Type': 'application/json' } },
|
|
),
|
|
),
|
|
)
|
|
|
|
const page = await fetchNotifications(
|
|
new ApiClient('egregoros.example', 'token'),
|
|
{ limit: 20 },
|
|
['favourite'],
|
|
)
|
|
|
|
expect(page.items).toEqual([{ id: 'favourite-1', type: 'favourite' }])
|
|
})
|
|
})
|