0ecb1ded78
Tests / frontend-test (push) Successful in 1m15s
Обсуждение заявки поверх штатных комментариев SharePoint (PnPjs item.comments) — без новых колонок и провижининга, права на переписку раздаёт сам SharePoint по правам на элемент. Встроено в ApproveFeature и EditFeature (единственные входы в просмотр заявки для всех типов), поэтому доступно автору, всем согласующим (включая уже решивших и ещё не дошедших до своего шага) и исполнителям, а не только текущему шагу маршрута, как DecisionPanel. Автора комментария всегда ставит реальная учётка — отладочная подмена личности на запись в SharePoint не влияет, в UI есть предупреждение. --------- Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/timur.gorodetskiy/request-portal/pulls/14
157 lines
5.9 KiB
TypeScript
157 lines
5.9 KiB
TypeScript
import {
|
|
ICommentSource,
|
|
buildCommentText,
|
|
hasAttachments,
|
|
toChatMessages
|
|
} from './chatMessages';
|
|
|
|
const ME = 'ivan@tnsu.kz';
|
|
|
|
function comment(over: Partial<ICommentSource>): ICommentSource {
|
|
return {
|
|
id: '1',
|
|
text: 'Сообщение',
|
|
createdDate: '2026-08-21T09:00:00Z',
|
|
author: { name: 'Иванов Иван', email: ME },
|
|
...over
|
|
};
|
|
}
|
|
|
|
describe('toChatMessages', () => {
|
|
it('сортирует от старых к новым — свежие в чате внизу', () => {
|
|
const messages = toChatMessages(
|
|
[
|
|
comment({ id: '2', createdDate: '2026-08-21T10:00:00Z', text: 'второе' }),
|
|
comment({ id: '1', createdDate: '2026-08-21T09:00:00Z', text: 'первое' })
|
|
],
|
|
ME
|
|
);
|
|
|
|
expect(messages.map((m) => m.text)).toEqual(['первое', 'второе']);
|
|
});
|
|
|
|
it('свои сообщения помечаются по email автора, регистр не важен', () => {
|
|
const messages = toChatMessages(
|
|
[
|
|
comment({ id: '1', author: { name: 'Иванов Иван', email: 'IVAN@TNSU.KZ' } }),
|
|
comment({ id: '2', author: { name: 'Петров Пётр', email: 'petr@tnsu.kz' } })
|
|
],
|
|
ME
|
|
);
|
|
|
|
expect(messages[0].isOwn).toBe(true);
|
|
expect(messages[1].isOwn).toBe(false);
|
|
});
|
|
|
|
it('без текущего пользователя своих сообщений нет (а не все свои)', () => {
|
|
const messages = toChatMessages([comment({})], '');
|
|
expect(messages[0].isOwn).toBe(false);
|
|
});
|
|
|
|
it('пустые комментарии отбрасываются — пустой «пузырь» показывать нечего', () => {
|
|
const messages = toChatMessages(
|
|
[comment({ id: '1', text: '' }), comment({ id: '2', text: ' ' }), comment({ id: '3' })],
|
|
ME
|
|
);
|
|
|
|
expect(messages).toHaveLength(1);
|
|
expect(messages[0].id).toBe('3');
|
|
});
|
|
|
|
it('дата без часового пояса читается как UTC, а не как местное время', () => {
|
|
const withZone = toChatMessages([comment({ createdDate: '2026-08-21T09:00:00Z' })], ME);
|
|
const withoutZone = toChatMessages([comment({ createdDate: '2026-08-21T09:00:00' })], ME);
|
|
|
|
expect(withoutZone[0].createdAt.getTime()).toBe(withZone[0].createdAt.getTime());
|
|
});
|
|
|
|
it('автор без ФИО подписывается email, без email — заглушкой', () => {
|
|
const messages = toChatMessages(
|
|
[
|
|
comment({ id: '1', author: { email: 'petr@tnsu.kz' } }),
|
|
comment({ id: '2', author: undefined })
|
|
],
|
|
ME
|
|
);
|
|
|
|
expect(messages[0].authorName).toBe('petr@tnsu.kz');
|
|
expect(messages[1].authorName).toBe('Неизвестный автор');
|
|
});
|
|
});
|
|
|
|
describe('файлы в сообщении', () => {
|
|
const FOLDER = '/sites/requests/Файлы чата/hr-vacation-12';
|
|
const FILE = '20260824-153012-1__акт.pdf';
|
|
const SECOND_FILE = '20260824-153012-2__скан.jpg';
|
|
|
|
it('пометка о файлах не показывается в тексте сообщения', () => {
|
|
const messages = toChatMessages(
|
|
[comment({ text: `Посмотрите акт\n[rp-files]${FILE}` })],
|
|
ME,
|
|
FOLDER
|
|
);
|
|
|
|
expect(messages[0].text).toBe('Посмотрите акт');
|
|
});
|
|
|
|
it('файл получает исходное имя и ссылку в папке заявки', () => {
|
|
const messages = toChatMessages(
|
|
[comment({ text: `[rp-files]${FILE}|${SECOND_FILE}` })],
|
|
ME,
|
|
FOLDER
|
|
);
|
|
|
|
expect(messages[0].text).toBe('');
|
|
expect(messages[0].attachments).toEqual([
|
|
{ fileName: 'акт.pdf', url: `${FOLDER}/${FILE}` },
|
|
{ fileName: 'скан.jpg', url: `${FOLDER}/${SECOND_FILE}` }
|
|
]);
|
|
});
|
|
|
|
it('сообщение из одних файлов не отбрасывается как пустое', () => {
|
|
const messages = toChatMessages([comment({ text: `[rp-files]${FILE}` })], ME, FOLDER);
|
|
expect(messages).toHaveLength(1);
|
|
});
|
|
|
|
it('пометка переживает перевод строки в формате Windows', () => {
|
|
const messages = toChatMessages([comment({ text: `Текст\r\n[rp-files]${FILE}` })], ME, FOLDER);
|
|
|
|
expect(messages[0].text).toBe('Текст');
|
|
expect(messages[0].attachments).toHaveLength(1);
|
|
});
|
|
|
|
it('обычное сообщение остаётся без вложений', () => {
|
|
const messages = toChatMessages([comment({ text: 'Просто текст' })], ME);
|
|
expect(messages[0].attachments).toEqual([]);
|
|
});
|
|
|
|
it('hasAttachments отличает переписку с файлами от переписки без них', () => {
|
|
expect(hasAttachments([comment({ text: 'Просто текст' })])).toBe(false);
|
|
expect(hasAttachments([comment({ text: `[rp-files]${FILE}` })])).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('buildCommentText', () => {
|
|
it('без файлов отдаёт очищенный текст', () => {
|
|
expect(buildCommentText(' Привет ', [])).toBe('Привет');
|
|
});
|
|
|
|
it('с файлами дописывает пометку отдельной строкой', () => {
|
|
expect(buildCommentText('Смотрите', ['a__акт.pdf', 'b__скан.jpg'])).toBe(
|
|
'Смотрите\n[rp-files]a__акт.pdf|b__скан.jpg'
|
|
);
|
|
});
|
|
|
|
it('без текста остаётся одна пометка — отправка «только файл»', () => {
|
|
expect(buildCommentText('', ['a__акт.pdf'])).toBe('[rp-files]a__акт.pdf');
|
|
});
|
|
|
|
it('собранный текст читается обратно без потерь', () => {
|
|
const raw = buildCommentText('Смотрите акт', ['20260824-153012-1__акт.pdf']);
|
|
const messages = toChatMessages([comment({ text: raw })], ME, '/f');
|
|
|
|
expect(messages[0].text).toBe('Смотрите акт');
|
|
expect(messages[0].attachments[0].fileName).toBe('акт.pdf');
|
|
});
|
|
});
|