Тесты и анимация кнопок (#4)
Tests / frontend-test (push) Successful in 1m9s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/timur.gorodetskiy/request-portal/pulls/4
This commit was merged in pull request #4.
This commit is contained in:
2026-08-07 11:53:48 +00:00
parent 3a15d83ded
commit f6f4872a7c
9 changed files with 905 additions and 13 deletions
@@ -0,0 +1,144 @@
import { IRoute, navigate, parseRoute, routeUrl } from './routing';
// buildQuery/navigate читают и пишут window.location через history API —
// сбрасываем адрес перед каждым тестом, чтобы они не зависели от порядка запуска.
beforeEach(() => {
window.history.replaceState({}, '', '/');
});
describe('parseRoute', () => {
it('пустой адрес — подача заявки в разделе «Новая заявка»', () => {
expect(parseRoute('')).toEqual({ mode: 'submit', nav: 'new' });
});
it('rpnav задаёт раздел бокового меню', () => {
expect(parseRoute('?rpnav=approvals').nav).toBe('approvals');
expect(parseRoute('?rpnav=own').nav).toBe('own');
expect(parseRoute('?rpnav=search').nav).toBe('search');
});
it('незнакомый rpnav откатывается на раздел по умолчанию', () => {
expect(parseRoute('?rpnav=bogus').nav).toBe('new');
});
it('полный набор параметров открывает заявку на согласование', () => {
expect(parseRoute('?rpmode=approve&rptype=hire&rpid=42')).toEqual({
mode: 'approve',
nav: 'new',
formKey: 'hire',
itemId: 42,
});
});
it('раздел меню сохраняется, когда поверх него открыта заявка', () => {
expect(parseRoute('?rpmode=approve&rptype=hire&rpid=42&rpnav=own').nav).toBe('own');
});
it('режим редактирования собственной заявки разбирается так же, как согласование', () => {
expect(parseRoute('?rpmode=edit&rptype=hire&rpid=7')).toEqual({
mode: 'edit',
nav: 'new',
formKey: 'hire',
itemId: 7,
});
});
it('без rpid заявка не открывается — остаётся обычная подача', () => {
expect(parseRoute('?rpmode=approve&rptype=hire').mode).toBe('submit');
});
it('нечисловой rpid не открывает заявку', () => {
expect(parseRoute('?rpmode=approve&rptype=hire&rpid=abc').mode).toBe('submit');
});
it('rpid=0 не считается заявкой (в SharePoint нумерация с единицы)', () => {
expect(parseRoute('?rpmode=approve&rptype=hire&rpid=0').mode).toBe('submit');
});
it('rptype + rpsource — создание производной заявки с предзаполнением', () => {
expect(parseRoute('?rptype=workplace&rpsource=15')).toEqual({
mode: 'create',
nav: 'new',
formKey: 'workplace',
sourceId: 15,
});
});
it('создание производной заявки всегда открывается в разделе «Новая заявка»', () => {
expect(parseRoute('?rptype=workplace&rpsource=15&rpnav=own').nav).toBe('new');
});
it('открытая заявка имеет приоритет над предзаполнением', () => {
expect(parseRoute('?rpmode=approve&rptype=hire&rpid=42&rpsource=15').mode).toBe('approve');
});
});
describe('routeUrl', () => {
it('раздел по умолчанию не засоряет адрес параметром', () => {
expect(routeUrl({ mode: 'submit', nav: 'new' })).toBe('/');
});
it('непустой раздел попадает в адрес', () => {
expect(routeUrl({ mode: 'submit', nav: 'own' })).toContain('rpnav=own');
});
it('сохраняет посторонние параметры страницы (их ставит SharePoint)', () => {
window.history.replaceState({}, '', '/page.aspx?env=WebViewList');
const url = routeUrl({ mode: 'submit', nav: 'own' });
expect(url).toContain('env=WebViewList');
expect(url).toContain('rpnav=own');
});
it('заявка без formKey/itemId не добавляет параметров открытия', () => {
const url = routeUrl({ mode: 'approve', nav: 'new' } as IRoute);
expect(url).not.toContain('rpmode');
});
});
describe('navigate', () => {
it('после перехода маршрут читается обратно тем же parseRoute (round-trip)', () => {
navigate({ mode: 'approve', nav: 'own', formKey: 'hire', itemId: 42 });
expect(parseRoute(window.location.search)).toEqual({
mode: 'approve',
nav: 'own',
formKey: 'hire',
itemId: 42,
});
});
it('round-trip для создания производной заявки', () => {
navigate({ mode: 'create', nav: 'new', formKey: 'workplace', sourceId: 15 });
expect(parseRoute(window.location.search)).toEqual({
mode: 'create',
nav: 'new',
formKey: 'workplace',
sourceId: 15,
});
});
it('возврат к подаче заявки убирает параметры открытой заявки из адреса', () => {
navigate({ mode: 'approve', nav: 'new', formKey: 'hire', itemId: 42 });
navigate({ mode: 'submit', nav: 'new' });
expect(window.location.search).toBe('');
});
it('добавляет запись в историю (pushState, а не replaceState)', () => {
const before = window.history.length;
navigate({ mode: 'submit', nav: 'own' });
expect(window.history.length).toBe(before + 1);
});
it('оповещает подписчиков своим событием (pushState сам popstate не порождает)', () => {
const seen: string[] = [];
const onChange = (): void => {
seen.push(window.location.search);
};
window.addEventListener('rp-route-change', onChange);
navigate({ mode: 'submit', nav: 'approvals' });
window.removeEventListener('rp-route-change', onChange);
expect(seen).toHaveLength(1);
expect(seen[0]).toContain('rpnav=approvals');
});
});