Files
request-portal/src/webparts/requestPortal/features/myRequests/pageCache.test.ts
T
Тимур Городецкий ea07e8d5ab feat: столбец «Исполнено» в таблицах заявок — кто и когда выполнил
Столбец «Исполнитель» показывает только текущего держателя, и было не видно,
кто из исполнителей уже отметил «Выполнено». Теперь видно ФИО и время
каждой отметки; столбец появляется, только если на странице есть такие.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:42:07 +05:00

146 lines
7.5 KiB
TypeScript

import { IMyRequest } from './models/IMyRequest';
import { IPage, pageKey, pageSignature, readPage, writePage } from './pageCache';
function request(over: Partial<IMyRequest> = {}): IMyRequest {
return {
id: 1,
code: '1-2608-1',
title: 'Заявка №1',
status: 'Черновик',
authorEmail: 'ivanov@tnsu.kz',
authorName: 'Иванов Иван',
created: '2026-08-07T10:00:00Z',
requestTypeId: 1,
requestTypeName: 'Приём на работу',
section: 'Кадры',
listId: '/sites/requests/Lists/List1',
siteUrl: 'https://tnsukz.sharepoint.com/sites/requests',
formKey: 'hire',
kind: 'own',
...over,
};
}
function page(over: Partial<IPage> = {}): IPage {
return { rows: [request()], totalRows: 1, ...over };
}
describe('pageKey', () => {
it('склеивает раздел и части ключа через разделитель', () => {
expect(pageKey('own', [1, 'Черновик'])).toBe('own|1|Черновик');
});
it('раздел всегда первый — ключи разных таблиц не пересекаются', () => {
expect(pageKey('own', [1])).not.toBe(pageKey('approvals', [1]));
});
it('разные части дают разные ключи', () => {
expect(pageKey('own', [1])).not.toBe(pageKey('own', [2]));
});
it('раздел без частей — просто раздел', () => {
expect(pageKey('search', [])).toBe('search');
});
});
describe('readPage / writePage', () => {
it('записанная страница читается по тому же ключу', () => {
const key = pageKey('own', ['read-write']);
const stored = page();
writePage(key, stored);
expect(readPage(key)).toBe(stored);
});
it('по незнакомому ключу возвращается undefined', () => {
expect(readPage(pageKey('own', ['nothing-here']))).toBeUndefined();
});
it('повторная запись по тому же ключу перезаписывает страницу', () => {
const key = pageKey('own', ['overwrite']);
writePage(key, page({ totalRows: 1 }));
writePage(key, page({ totalRows: 2 }));
expect(readPage(key)?.totalRows).toBe(2);
});
it('кеш переживает «размонтирование» — хранилище на уровне модуля, а не компонента', () => {
const key = pageKey('approvals', ['module-level']);
writePage(key, page({ totalRows: 7 }));
// Между записью и чтением компонент таблицы мог быть уничтожен целиком —
// на модульное хранилище это не влияет (ради этого оно и вынесено из useRef).
expect(readPage(key)?.totalRows).toBe(7);
});
});
describe('pageSignature', () => {
it('одинаковое содержимое даёт одинаковую подпись (перерисовывать нечего)', () => {
expect(pageSignature(page())).toBe(pageSignature(page()));
});
it('изменение общего числа строк меняет подпись', () => {
expect(pageSignature(page({ totalRows: 1 }))).not.toBe(pageSignature(page({ totalRows: 2 })));
});
it('изменение статуса заявки меняет подпись', () => {
const before = page({ rows: [request({ status: 'Черновик' })] });
const after = page({ rows: [request({ status: 'На согласовании' })] });
expect(pageSignature(before)).not.toBe(pageSignature(after));
});
it('изменение порядка строк меняет подпись', () => {
const a = request({ id: 1 });
const b = request({ id: 2 });
expect(pageSignature(page({ rows: [a, b], totalRows: 2 }))).not.toBe(
pageSignature(page({ rows: [b, a], totalRows: 2 }))
);
});
it('поля, которых нет в таблице, на подпись не влияют', () => {
// kind/section/formKey в колонках не показываются — их изменение не должно
// приводить к перерисовке (иначе таблица моргала бы на каждом фоновом обновлении).
const before = page({ rows: [request({ section: 'Кадры', kind: 'own' })] });
const after = page({ rows: [request({ section: 'ИТ', kind: 'approval' })] });
expect(pageSignature(before)).toBe(pageSignature(after));
});
it('изменение комментария к решению меняет подпись — он показан колонкой «Причина возврата/отклонения»', () => {
const before = page({ rows: [request({ decisionComment: 'Исправьте должность' })] });
const after = page({ rows: [request({ decisionComment: 'Приложите скан' })] });
expect(pageSignature(before)).not.toBe(pageSignature(after));
});
it('появление комментария к решению у строки без него тоже меняет подпись', () => {
const before = page({ rows: [request()] });
const after = page({ rows: [request({ decisionComment: 'Исправьте должность' })] });
expect(pageSignature(before)).not.toBe(pageSignature(after));
});
it('появление кода заявки меняет подпись — он показан колонкой «Код»', () => {
// Код проставляется вторым запросом, уже после создания элемента (см.
// saveRequestItem), поэтому первая выдача приходит без него. Не учитывать
// его в подписи — значит оставить в таблице «#1» до перезагрузки страницы.
const before = page({ rows: [request({ code: '' })] });
const after = page({ rows: [request({ code: '1-2608-1' })] });
expect(pageSignature(before)).not.toBe(pageSignature(after));
});
it('смена текущего исполнителя меняет подпись — он показан колонкой «Исполнитель»', () => {
// Заявка ушла на следующий шаг: статус тот же («На согласовании»), а держатель другой.
const before = page({ rows: [request({ currentAssignees: [{ name: 'Петров Пётр', email: 'petrov@tnsu.kz' }] })] });
const after = page({ rows: [request({ currentAssignees: [{ name: 'Сидорова Анна', email: 'sidorova@tnsu.kz' }] })] });
expect(pageSignature(before)).not.toBe(pageSignature(after));
});
it('новый выполнивший меняет подпись — он показан колонкой «Исполнено»', () => {
// Один из параллельных исполнителей отметил «Выполнено»: статус заявки тот же («Выполнение»).
const before = page({ rows: [request({ executedSteps: [] })] });
const after = page({
rows: [request({ executedSteps: [{ name: 'Петров Пётр', email: 'petrov@tnsu.kz', executedAt: '2026-10-08T05:00:00Z' }] })],
});
expect(pageSignature(before)).not.toBe(pageSignature(after));
});
it('пустая страница имеет стабильную подпись', () => {
expect(pageSignature({ rows: [], totalRows: 0 })).toBe(pageSignature({ rows: [], totalRows: 0 }));
});
});