Files

200 lines
6.0 KiB
TypeScript
Raw Permalink Normal View History

import { afterEach, describe, expect, it } from 'vitest';
import { act } from '@testing-library/react';
import { parseGatewaySnapshot } from '@/api/gateway';
import type { GuildSummary } from '@/api/types';
import { dispatchGatewayEvent, useGatewayStore } from '@/stores/gateway';
import { useSessionStore } from '@/stores/session';
import { makeReadySnapshot } from './helpers';
afterEach(() => {
useSessionStore.getState().reset();
useGatewayStore.setState({
status: 'idle',
heartbeatIntervalMs: null,
sessionId: null,
attempts: 0,
invalidated: false,
});
});
describe('session store', () => {
it('applies a READY snapshot: guilds, channels and session id', () => {
const snapshot = parseGatewaySnapshot(
makeReadySnapshot([
{
id: 'g-1',
name: 'Main',
is_main: true,
channels: [
{ id: 'c-1', name: 'general', position: 0 },
{ id: 'c-2', name: 'voice', type: 'voice', position: 1 },
],
},
]),
);
expect(snapshot).not.toBeNull();
act(() => {
dispatchGatewayEvent({
op: 0,
t: 'READY',
s: 1,
d: makeReadySnapshot([
{
id: 'g-1',
name: 'Main',
is_main: true,
channels: [
{ id: 'c-1', name: 'general', position: 0 },
{ id: 'c-2', name: 'voice', type: 'voice', position: 1 },
],
},
]),
});
});
const state = useSessionStore.getState();
expect(state.guilds).toHaveLength(1);
expect(state.guilds[0]?.name).toBe('Main');
expect(state.guilds[0]?.channels.map((channel) => channel.id)).toEqual(['c-1', 'c-2']);
expect(state.sessionId).toBe('session-1');
expect(state.user?.display_name).toBe('Alice');
expect(useGatewayStore.getState().status).toBe('connected');
expect(useGatewayStore.getState().heartbeatIntervalMs).toBe(45_000);
});
it('не затирает данные снапшота неполной сводкой REST', () => {
act(() => {
dispatchGatewayEvent({
op: 0,
t: 'READY',
s: 1,
d: makeReadySnapshot([
{
id: 'g-1',
name: 'Main',
owner_id: 'user-9',
is_main: true,
my_permissions: ['MANAGE_WEBHOOKS'],
channels: [{ id: 'c-1', name: 'general', position: 0 }],
},
]),
});
});
// Ответ REST пришёл без части полей: undefined не должен перетирать снапшот.
act(() => {
useSessionStore.getState().upsertGuild({
id: 'g-1',
name: 'Main из REST',
owner_id: undefined,
is_main: undefined,
member_count: undefined,
my_role_ids: undefined,
my_permissions: undefined,
} as unknown as GuildSummary);
});
const guild = useSessionStore.getState().guilds[0];
expect(guild?.name).toBe('Main из REST');
expect(guild?.owner_id).toBe('user-9');
expect(guild?.is_main).toBe(true);
expect(guild?.my_permissions).toEqual(['MANAGE_WEBHOOKS']);
expect(guild?.channels.map((channel) => channel.id)).toEqual(['c-1']);
});
it('keeps channels with can_send=false (only can_view=false is hidden)', () => {
act(() => {
dispatchGatewayEvent({
op: 0,
t: 'READY',
s: 2,
d: makeReadySnapshot([
{
id: 'g-1',
name: 'Main',
channels: [
{ id: 'c-readonly', name: 'announcements', can_send: false },
{ id: 'c-hidden', name: 'secret', can_view: false },
],
},
]),
});
});
const channels = useSessionStore.getState().guilds[0]?.channels ?? [];
expect(channels.map((channel) => channel.id)).toEqual(['c-readonly', 'c-hidden']);
});
it('ignores unknown events and malformed snapshots', () => {
act(() => {
dispatchGatewayEvent({ op: 0, t: 'SOMETHING_NEW', s: 3, d: { anything: true } });
dispatchGatewayEvent({ op: 0, t: 'READY', s: 4, d: { guilds: [] } });
});
expect(useSessionStore.getState().guilds).toEqual([]);
expect(useSessionStore.getState().user).toBeNull();
});
it('drops a guild and clears the selection when leaving', () => {
act(() => {
dispatchGatewayEvent({
op: 0,
t: 'READY',
s: 5,
d: makeReadySnapshot([{ id: 'g-1', name: 'Main' }]),
});
useSessionStore.getState().selectChannel('g-1', null);
useSessionStore.getState().removeGuild('g-1');
});
const state = useSessionStore.getState();
expect(state.guilds).toEqual([]);
expect(state.selectedGuildId).toBeNull();
});
it('хранит выбранный в настройках сервер отдельно от выбора в приложении', () => {
act(() => {
dispatchGatewayEvent({
op: 0,
t: 'READY',
s: 6,
d: makeReadySnapshot([
{ id: 'g-1', name: 'Main' },
{ id: 'g-2', name: 'Second' },
]),
});
useSessionStore.getState().selectChannel('g-1', 'c-1');
useSessionStore.getState().selectSettingsGuild('g-2');
});
const state = useSessionStore.getState();
expect(state.settingsGuildId).toBe('g-2');
// Выбор в приложении не пострадал.
expect(state.selectedGuildId).toBe('g-1');
act(() => {
useSessionStore.getState().reset();
});
expect(useSessionStore.getState().settingsGuildId).toBeNull();
});
it('обновляет свой никнейм на сервере', () => {
act(() => {
dispatchGatewayEvent({
op: 0,
t: 'READY',
s: 7,
d: makeReadySnapshot([{ id: 'g-1', name: 'Main', my_nickname: 'Аля' }]),
});
});
expect(useSessionStore.getState().guilds[0]?.my_nickname).toBe('Аля');
act(() => {
useSessionStore.getState().setMyNickname('g-1', 'Алиса');
});
expect(useSessionStore.getState().guilds[0]?.my_nickname).toBe('Алиса');
});
});