fix(install): LIVEKIT_URL попадает в compose приложения

Шаблон compose подставляет переменные из явного списка: LIVEKIT_PUBLIC_URL не
был в нём, поэтому приложение получало пустой адрес LiveKit и голос оставался
выключенным. Добавлены LIVEKIT_API_KEY/SECRET/PUBLIC_URL в рендер compose.
This commit is contained in:
2026-09-20 18:19:19 +03:00
parent ca34d05377
commit 2749a0e831
13 changed files with 73 additions and 60 deletions
+12 -13
View File
@@ -198,11 +198,7 @@ describe('кастомные эмодзи: зеркало композера', (
const mirrorRef = createRef<HTMLDivElement>();
render(
<ComposerMirror
value="<:pepe:900>"
selection={{ start: 0, end: 0 }}
mirrorRef={mirrorRef}
/>,
<ComposerMirror value="<:pepe:900>" selection={{ start: 0, end: 0 }} mirrorRef={mirrorRef} />,
);
expect(mirrorRef.current?.querySelector('img')).toBeNull();
@@ -217,7 +213,12 @@ describe('кастомные эмодзи: пикер', () => {
const picked: string[] = [];
render(
<EmojiPicker open guildId="g-1" onClose={() => undefined} onPick={(emoji) => picked.push(emoji)} />,
<EmojiPicker
open
guildId="g-1"
onClose={() => undefined}
onPick={(emoji) => picked.push(emoji)}
/>,
);
const section = screen.getByTestId('emoji-picker-server');
@@ -229,16 +230,16 @@ describe('кастомные эмодзи: пикер', () => {
await visitor.type(screen.getByLabelText('Поиск эмодзи сервера'), 'party');
expect(within(section).queryByText(':pepe:')).toBeNull();
await visitor.click(within(section).getByRole('button', { name: 'Выбрать эмодзи party_parrot' }));
await visitor.click(
within(section).getByRole('button', { name: 'Выбрать эмодзи party_parrot' }),
);
expect(picked).toEqual(['<a:party_parrot:901>']);
});
it('без сервера секция эмодзи сервера не показывается', () => {
useSessionStore.setState({ guilds: [guildWithEmojis()] });
render(
<EmojiPicker open guildId={null} onClose={() => undefined} onPick={() => undefined} />,
);
render(<EmojiPicker open guildId={null} onClose={() => undefined} onPick={() => undefined} />);
expect(screen.queryByTestId('emoji-picker-server')).toBeNull();
});
@@ -473,9 +474,7 @@ describe('кастомные эмодзи: реакции', () => {
describe('кастомные эмодзи: снапшот и событие шлюза', () => {
it('читает эмодзи из READY и обновляет их по GUILD_EMOJIS_UPDATE', () => {
const snapshot = makeReadySnapshot([
{ id: 'g-1', name: 'Сервер', emojis: [pepe] },
]);
const snapshot = makeReadySnapshot([{ id: 'g-1', name: 'Сервер', emojis: [pepe] }]);
dispatchGatewayEvent({ op: 0, t: 'READY', s: 1, d: snapshot });
expect(useSessionStore.getState().guilds[0]?.emojis).toEqual([pepe]);
+22 -4
View File
@@ -68,7 +68,8 @@ function routes(options: RoutesOptions = {}) {
{
match: '/api/v1/guilds/g-1/emojis',
method: 'POST',
response: options.uploadResponse ?? (() => json({ emoji: { ...pepe, id: 'e-3', name: 'kek' } })),
response:
options.uploadResponse ?? (() => json({ emoji: { ...pepe, id: 'e-3', name: 'kek' } })),
},
{
match: '/api/v1/users/@me/guilds',
@@ -169,6 +170,25 @@ describe('настройки сервера: секция «Эмодзи»', ()
});
});
it('принимает файл перетаскиванием в область загрузки', async () => {
const visitor = userEvent.setup();
const fetchMock = installFetch(routes());
const { dialog } = await openEmojisSection();
fireEvent.drop(within(dialog).getByTestId('emoji-dropzone'), {
dataTransfer: { files: [emojiFile('drop.png', 'image/png')] },
});
// Имя файла видно в области загрузки — файл принят.
expect(await within(dialog).findByText(/drop\.png/)).toBeVisible();
await visitor.type(within(dialog).getByLabelText('Имя эмодзи'), 'drop');
await visitor.click(within(dialog).getByRole('button', { name: 'Загрузить' }));
await waitFor(() => {
expect(uploadForm(fetchMock)?.get('name')).toBe('drop');
});
});
it('не отправляет файл неподдерживаемого формата', async () => {
const fetchMock = installFetch(routes());
@@ -213,9 +233,7 @@ describe('настройки сервера: секция «Эмодзи»', ()
);
await visitor.click(within(dialog).getByRole('button', { name: 'Загрузить' }));
expect(await within(dialog).findByRole('alert')).toHaveTextContent(
'Имя эмодзи: 2–32 символа',
);
expect(await within(dialog).findByRole('alert')).toHaveTextContent('Имя эмодзи: 2–32 символа');
expect(uploadForm(fetchMock)).toBeUndefined();
});
+14 -5
View File
@@ -5,7 +5,16 @@ import type { Message } from '@/api/messages';
import { MessageList } from '@/components/chat/MessageList';
import type { AuthorDirectory } from '@/components/chat/useAuthorDirectory';
import { parseSystemMessage, systemMessageUserId } from '@/lib/systemMessage';
import { makeMessage, makeUser, json, installFetch, installGatewaySocket, messagesRoutes, renderApp, resetStores } from './helpers';
import {
makeMessage,
makeUser,
json,
installFetch,
installGatewaySocket,
messagesRoutes,
renderApp,
resetStores,
} from './helpers';
/**
* Системные сообщения (AGENT.md §7.6): сервер пишет их с `type: "system"`,
@@ -104,9 +113,7 @@ describe('системные сообщения: лента', () => {
it('member_kick — «{имя} покинул сервер (исключён)»', () => {
renderList([systemMessage('{"event":"member_kick","user_id":"user-2"}')]);
expect(screen.getByTestId('message-m-1')).toHaveTextContent(
'Боб покинул сервер (исключён)',
);
expect(screen.getByTestId('message-m-1')).toHaveTextContent('Боб покинул сервер (исключён)');
});
it('channel_created — комната и голосовая комната', () => {
@@ -127,7 +134,9 @@ describe('системные сообщения: лента', () => {
systemMessage('Аля вошла', 'm-2'),
]);
expect(screen.getByTestId('message-m-1')).toHaveTextContent('{"event":"guild_boost","level":2}');
expect(screen.getByTestId('message-m-1')).toHaveTextContent(
'{"event":"guild_boost","level":2}',
);
expect(screen.getByTestId('message-m-2')).toHaveTextContent('Аля вошла');
});