Files
glchat/web/tests/i18n.test.ts
T
grendervill bcfc275bb3 feat(web): друзья, личные беседы и вход только по приглашению
Раздел «Друзья» по запросу пользователя:

- первая иконка рейки — «Друзья», серверы ниже за разделителем; личные беседы
  не засоряют список серверов;
- сайдбар друзей: беседы с точками статуса и бейджами непрочитанного,
  список друзей с кликом на беседу;
- контент: пустое состояние «Добавь друзей для общения» с поиском, поиск по
  логину (debounce), добавление в друзья, входящие и исходящие заявки,
  меню «…» (удалить/заблокировать), поповер последнего входа в часовом поясе
  пользователя (по умолчанию МСК), блок «Присоединиться по приглашению»
  с предпросмотром сервера;
- личный чат на существующих компонентах: права по can_send канала, заголовок
  с именем и статусом собеседника, поиск, пины, ack и typing работают;
- realtime: READY dm_channels, события DM_CHANNEL_CREATE, RELATIONSHIP_UPDATE,
  PRESENCE_UPDATE и MESSAGE_CREATE в беседах;
- новый пользователь: «У вас пока нет серверов», создание сервера и вход по
  приглашению (автовступления нет);
- 239 тестов Vitest (было 219), тест паритета ключей i18n (527 ключей).
2026-09-20 01:52:27 +03:00

46 lines
1.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import en from '@/i18n/locales/en.json';
import ru from '@/i18n/locales/ru.json';
/** Плоский список пар «ключ → значение». */
function flatEntries(value: unknown, prefix = ''): [string, unknown][] {
if (typeof value !== 'object' || value === null) {
return [];
}
const entries: [string, unknown][] = [];
for (const [key, nested] of Object.entries(value)) {
const path = prefix === '' ? key : `${prefix}.${key}`;
if (typeof nested === 'object' && nested !== null) {
entries.push(...flatEntries(nested, path));
} else {
entries.push([path, nested]);
}
}
return entries;
}
/** Плоский список ключей вида `friends.empty.title`. */
function flatKeys(value: unknown): string[] {
return flatEntries(value)
.map(([key]) => key)
.sort();
}
/**
* Паритет переводов: русский — основной, английский должен повторять его
* ключ в ключ, иначе часть интерфейса останется без строки.
*/
describe('переводы', () => {
it('ключи ru и en совпадают', () => {
expect(flatKeys(en)).toEqual(flatKeys(ru));
});
it('нет пустых строк', () => {
const empty = flatEntries(ru)
.filter(([, value]) => typeof value === 'string' && value.trim() === '')
.map(([key]) => key);
expect(empty).toEqual([]);
});
});