feat: улучшить учебные периоды и просмотр расписаний
This commit is contained in:
64
frontend/tests/academic-year-builder.test.mjs
Normal file
64
frontend/tests/academic-year-builder.test.mjs
Normal file
@@ -0,0 +1,64 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
import {
|
||||
academicYearDefaults,
|
||||
activityColorClass,
|
||||
activityColorCode,
|
||||
validateAcademicYearSemesters
|
||||
} from '../admin/js/views/academic-calendar.js';
|
||||
|
||||
test('окончание, название и границы семестров рассчитываются по году начала', () => {
|
||||
assert.deepEqual(academicYearDefaults('2026'), {
|
||||
startYear: 2026,
|
||||
endYear: 2027,
|
||||
title: '2026-2027',
|
||||
startDate: '2026-09-01',
|
||||
endDate: '2027-06-30',
|
||||
semesters: [
|
||||
{ semesterType: 'autumn', startDate: '2026-09-01', endDate: '2027-01-31' },
|
||||
{ semesterType: 'spring', startDate: '2027-02-01', endDate: '2027-06-30' }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test('год начала должен состоять ровно из четырёх цифр', () => {
|
||||
assert.throws(() => academicYearDefaults('26'), /четырёх цифр/);
|
||||
assert.throws(() => academicYearDefaults('202A'), /четырёх цифр/);
|
||||
assert.throws(() => academicYearDefaults('9999'), /диапазоне/);
|
||||
});
|
||||
|
||||
test('учебный год нельзя подготовить без семестров', () => {
|
||||
assert.throws(
|
||||
() => validateAcademicYearSemesters([], '2026-09-01', '2027-06-30'),
|
||||
/Учебный год нельзя создать без семестров/
|
||||
);
|
||||
});
|
||||
|
||||
test('пересекающиеся семестры отклоняются до отправки формы', () => {
|
||||
assert.throws(
|
||||
() => validateAcademicYearSemesters([
|
||||
{ semesterType: 'autumn', startDate: '2026-09-01', endDate: '2027-02-10' },
|
||||
{ semesterType: 'spring', startDate: '2027-02-01', endDate: '2027-06-30' }
|
||||
], '2026-09-01', '2027-06-30'),
|
||||
/Периоды семестров не должны пересекаться/
|
||||
);
|
||||
});
|
||||
|
||||
test('цвет обозначения берётся из справочника и имеет безопасный запасной вариант', () => {
|
||||
assert.equal(activityColorCode({ colorCode: '#f59e0b' }), '#f59e0b');
|
||||
assert.equal(activityColorClass({ colorCode: '#f59e0b' }), 'calendar-activity-color-f59e0b');
|
||||
assert.equal(activityColorCode({ colorCode: 'red; background: black' }), '#64748b');
|
||||
});
|
||||
|
||||
test('форма создания не содержит поля названия и включает оба семестра', async () => {
|
||||
const view = await readFile(new URL('../admin/views/academic-calendar.html', import.meta.url), 'utf8');
|
||||
|
||||
assert.doesNotMatch(view, /id="academic-year-title"/);
|
||||
assert.match(view, /id="academic-year-start"[^>]+maxlength="4"[^>]+pattern="\[0-9\]\{4\}"/);
|
||||
assert.match(view, /id="academic-year-end"[^>]+maxlength="4"[^>]+readonly/);
|
||||
assert.match(view, /id="academic-year-autumn-start"/);
|
||||
assert.match(view, /id="academic-year-spring-start"/);
|
||||
assert.match(view, /Сохранить учебный год и семестры/);
|
||||
});
|
||||
@@ -2,7 +2,9 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import {
|
||||
findDefaultSemester,
|
||||
findSemesterForDate,
|
||||
formatLessonWeeksText,
|
||||
scheduleRangeForSemester,
|
||||
semesterOptionLabel
|
||||
} from '../admin/js/views/schedule-view.js';
|
||||
@@ -31,20 +33,29 @@ test('семестр определяется по выбранной дате
|
||||
assert.equal(findSemesterForDate(semesters, '2026-07-01'), null);
|
||||
});
|
||||
|
||||
test('при выборе другого семестра диапазон начинается с его первой учебной недели', () => {
|
||||
test('вне учебного периода по умолчанию выбирается последний доступный семестр', () => {
|
||||
assert.equal(findDefaultSemester([springSemester, autumnSemester], '2026-08-14')?.id, 12);
|
||||
});
|
||||
|
||||
test('при выборе семестра диапазон охватывает его целиком', () => {
|
||||
assert.deepEqual(scheduleRangeForSemester('2026-07-21', autumnSemester), {
|
||||
startDate: '2025-09-01',
|
||||
endDate: '2025-09-14'
|
||||
endDate: '2026-01-31'
|
||||
});
|
||||
});
|
||||
|
||||
test('внутри выбранного семестра сохраняется текущая двухнедельная точка просмотра', () => {
|
||||
test('дата внутри выбранного семестра не сужает его до двух недель', () => {
|
||||
assert.deepEqual(scheduleRangeForSemester('2026-03-18', springSemester), {
|
||||
startDate: '2026-03-16',
|
||||
endDate: '2026-03-29'
|
||||
startDate: '2026-02-09',
|
||||
endDate: '2026-06-30'
|
||||
});
|
||||
});
|
||||
|
||||
test('диапазон недель занятия всегда показывается явно', () => {
|
||||
assert.equal(formatLessonWeeksText(1, 18), '(с 1 по 18 нед.)');
|
||||
assert.equal(formatLessonWeeksText(7, 7), '(7 нед.)');
|
||||
});
|
||||
|
||||
test('название семестра содержит учебный год, тип и границы', () => {
|
||||
assert.equal(
|
||||
semesterOptionLabel(springSemester),
|
||||
|
||||
33
frontend/tests/schedule-visual-menu.test.mjs
Normal file
33
frontend/tests/schedule-visual-menu.test.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
import { calculateVisualContextMenuPosition } from '../admin/js/views/schedule.js';
|
||||
|
||||
test('меню пары открывается справа от кнопки с тремя точками', () => {
|
||||
assert.deepEqual(calculateVisualContextMenuPosition({
|
||||
anchorRect: { left: 100, right: 120, top: 80, bottom: 100 },
|
||||
menuWidth: 220,
|
||||
menuHeight: 120,
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720
|
||||
}), { left: 128, top: 80 });
|
||||
});
|
||||
|
||||
test('у края экрана меню раскрывается влево и остаётся в viewport', () => {
|
||||
assert.deepEqual(calculateVisualContextMenuPosition({
|
||||
anchorRect: { left: 1240, right: 1260, top: 680, bottom: 700 },
|
||||
menuWidth: 220,
|
||||
menuHeight: 160,
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720
|
||||
}), { left: 1012, top: 540 });
|
||||
});
|
||||
|
||||
test('контекстное меню больше не центрируется стилями', async () => {
|
||||
const styles = await readFile(new URL('../admin/css/components.css', import.meta.url), 'utf8');
|
||||
const menuRule = styles.match(/\.schedule-visual-context-menu\s*\{([\s\S]*?)\}/)?.[1] || '';
|
||||
|
||||
assert.doesNotMatch(menuRule, /translate\(-50%,\s*-50%\)/);
|
||||
assert.match(menuRule, /position:\s*fixed/);
|
||||
});
|
||||
Reference in New Issue
Block a user