feat: завершить миграцию фронтенда на React (этапы 1-5)
Админка, настройки, логин, кабинеты преподавателя и студента полностью переведены на React 19 (модель strangler, docs/REACT_MIGRATION.md). - 15 вкладок админки: lazy-загрузка, общий error boundary, сервисный слой - Удалены legacy-views и переходный слой LegacyTabView (45 файлов) - Сборка esbuild (frontend/scripts/build-react.mjs) с бюджетами gzip на чанки - CSP-хэши, кэширование: chunk'и immutable, CSS/HTML no-cache (react-cache.conf) - Фиксы: lazy-импорт именованных экспортов (краш после авторизации), растяжение #admin-root/#settings-root на всю ширину, возврат subjects/lessonTypes из справочников (краш просмотра расписаний), отказ от заведомо широкого поиска расписания без выбранной цели, центрирование карточки логина - Тесты: 237/237 (npm run check), регрессионные на lazy-вкладки и ширину - Документация: REACT_MIGRATION.md, REACT_MIGRATION_RESULT.md, FRONTEND.md
This commit is contained in:
56
frontend/tests/react-login.test.mjs
Normal file
56
frontend/tests/react-login.test.mjs
Normal file
@@ -0,0 +1,56 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import {
|
||||
LOGIN_ERROR_MESSAGES,
|
||||
requestLogin,
|
||||
validateLogin
|
||||
} from '../react/login/login-service.js';
|
||||
|
||||
test('React-форма входа валидирует обязательные поля и длину пароля', () => {
|
||||
assert.deepEqual(validateLogin(), {
|
||||
password: 'Введите пароль',
|
||||
username: 'Введите имя пользователя'
|
||||
});
|
||||
assert.deepEqual(validateLogin({ username: 'admin', password: '123' }), {
|
||||
password: 'Минимум 4 символа'
|
||||
});
|
||||
assert.deepEqual(validateLogin({ username: ' admin ', password: '1234' }), {});
|
||||
});
|
||||
|
||||
test('React-форма сохраняет прежний контракт POST /api/auth/login', async () => {
|
||||
const calls = [];
|
||||
const result = await requestLogin(
|
||||
{ username: ' admin ', password: 'секрет' },
|
||||
async (url, options) => {
|
||||
calls.push({ options, url });
|
||||
return new Response(JSON.stringify({
|
||||
redirect: '/admin/',
|
||||
role: 'ADMIN',
|
||||
token: 'access-token'
|
||||
}), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
status: 200
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(calls[0].url, '/api/auth/login');
|
||||
assert.equal(calls[0].options.method, 'POST');
|
||||
assert.equal(calls[0].options.credentials, 'same-origin');
|
||||
assert.deepEqual(JSON.parse(calls[0].options.body), {
|
||||
password: 'секрет',
|
||||
username: 'admin'
|
||||
});
|
||||
});
|
||||
|
||||
test('React-форма использует безопасное русское сообщение при не-JSON ошибке', async () => {
|
||||
const result = await requestLogin(
|
||||
{ username: 'admin', password: '1234' },
|
||||
async () => new Response('Ошибка шлюза', { status: 502 })
|
||||
);
|
||||
|
||||
assert.equal(result.ok, false);
|
||||
assert.equal(result.message, LOGIN_ERROR_MESSAGES.credentials);
|
||||
});
|
||||
Reference in New Issue
Block a user