ea07e8d5ab
Столбец «Исполнитель» показывает только текущего держателя, и было не видно, кто из исполнителей уже отметил «Выполнено». Теперь видно ФИО и время каждой отметки; столбец появляется, только если на странице есть такие. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
146 lines
7.5 KiB
TypeScript
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 }));
|
|
});
|
|
});
|