Files
psu-schedule/test/timetable.test.js
Zakhar Timoshenko 15851b26cd feat: Enhance session handling in timetable and UI
- Introduced session types (exams, credits, consultations, coursework defenses) with distinct icons and colors for better visibility in the UI.
- Updated PlansScreen, ScheduleScreen, and WeekGrid to display session information appropriately.
- Modified CalendarView and PlansView to include session indicators in the calendar and plans.
- Enhanced LessonCard and CompactLesson to show session details and adjust styling based on session presence.
- Added new SessionMark component for consistent session representation across Android and iOS.
- Updated server logic to handle session entries from external documents, ensuring accurate timetable representation.
- Improved date parsing to accommodate session-related dates in various formats.
- Added tests to verify session handling and date parsing functionality.
2026-10-19 20:49:13 +03:00

116 lines
6.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { parseDocx } from "../server/parser.js";
import { parseWeeksDocx, parseBellsDocx } from "../server/calendar.js";
import { mergeWeekTables } from "../server/source.js";
import { timetable } from "../server/timetable.js";
const fixture = (name) =>
readFileSync(new URL(`./fixtures/${name}.docx`, import.meta.url));
const calendar = {
...mergeWeekTables([parseWeeksDocx(fixture("weeks"))]),
bells: parseBellsDocx(fixture("bells")),
source: {},
};
const schedule = parseDocx(fixture("fef-23-ba"), { name: "23-ЛГ_23-БА.docx" });
const file = { id: "fef", name: "23-ЛГ_23-БА.docx", faculty: "ФЭФ", course: "4 курс" };
test("Расписание группы по датам: недели, пары, подгруппы, пустые выходные", () => {
const t = timetable({ file, schedule, calendar, group: "23-БА", from: "2026-10-16", days: 3 });
assert.equal(t.semester.label, "Осенний семестр 2026/27");
assert.deepEqual(t.days.map((d) => [d.date, d.weekday, d.week.n, d.week.color]), [
["2026-10-16", 5, 7, "white"],
["2026-10-17", 6, 7, "white"],
["2026-10-18", 7, 7, "white"],
]);
// Friday of week 7: "12-15н." and "16н." lessons are not on this date.
assert.deepEqual(
t.days[0].lessons.map((l) => [l.start, l.pair, l.subject]),
[
["08:30", 1, "Инновационный менеджмент"],
["10:05", 2, "PR-менеджмент"],
["11:45", 3, "Бизнес-анализ"],
],
);
assert.equal(t.days[1].lessons.length, 0);
assert.ok(t.weekly.length >= 5, "строки Word по дням недели доступны всегда");
const lg = timetable({ file, schedule, calendar, group: "23-ЛГ", from: "2026-10-12", days: 7 });
assert.deepEqual(lg.subgroups, ["Подгруппа 1", "Подгруппа 2"]);
assert.ok(lg.days.flatMap((d) => d.lessons).some((l) => l.subgroup === "Подгруппа 2"));
});
test("Без семестра в таблице недель даты не угадываются", () => {
const spring = { ...schedule, period: "Весенний семестр" };
const t = timetable({ file, schedule: spring, calendar, group: "23-БА", from: "2027-03-01", days: 7 });
assert.equal(t.semester, null);
assert.ok(t.note);
assert.ok(t.days.every((d) => d.lessons.length === 0 && d.week === null));
assert.throws(
() => timetable({ file, schedule, calendar, group: "99-ХХ", from: "2026-10-16", days: 1 }),
/нет группы/,
);
});
test("range=semester: все дни текущего семестра от первого до последнего", () => {
const t = timetable({ file, schedule, calendar, group: "23-БА", from: "2026-10-16", range: "semester", today: "2026-10-16" });
assert.equal(t.days[0].date, t.semester.start);
assert.equal(t.days.at(-1).date, t.semester.end);
assert.ok(t.days.length > 62, "больше обычного предела в 62 дня");
});
test("Сессия с датами в Word не зависит от таблицы недель", () => {
// June 2026 lies outside the 2026/27 week table, yet every lesson has its own date.
const session = parseDocx(fixture("itzs-summer-2026"), { name: "23-ИТзс.docx" });
const t = timetable({
file: { id: "itzs", name: "23-ИТзс.docx" },
schedule: session,
calendar,
group: "23-ИТзс",
from: "2026-10-19",
range: "semester",
today: "2026-10-19",
});
assert.deepEqual(t.semester, {
id: "session-2026-06-08",
label: "Лабораторно-экзаменационная сессия",
start: "2026-06-08",
end: "2026-06-27",
});
assert.equal(t.note, null);
assert.equal(t.days.length, 20);
assert.equal(t.days.reduce((n, d) => n + d.lessons.length, 0), 58);
const exam = t.days.find((d) => d.date === "2026-06-18").lessons.find((l) => l.type === "Экзамен");
assert.deepEqual([exam.start, exam.end, exam.pair], ["08:30", "09:55", 1]);
});
test("Зачёты из отдельного документа попадают в дни расписания группы", async () => {
const { buildIndex, sessionEntriesOf } = await import("../server/directory.js");
// The 26-АРХ credit schedule, readdressed to 23-БА, plus one exam after the semester's end.
const credits = parseDocx(fixture("isf-credits-26-arh"), { name: "Зачеты.docx" });
const readdress = (l) => ({ ...l, groups: ["23-БА"], subgroups: l.subgroups.map((s) => ({ ...s, group: "23-БА" })) });
const lessons = credits.lessons.map(readdress);
lessons.push({ ...lessons[1], id: "late", date: "2027-01-15", type: "Экзамен", subject: "Бизнес-анализ" });
const idx = buildIndex([
{ id: "fef", file, schedule },
{ id: "credits", file: { id: "credits", name: "Зачеты.docx" }, schedule: { ...credits, lessons, groups: ["23-БА"] } },
]);
const session = sessionEntriesOf(idx, "23-БА", null, "fef");
assert.equal(session.length, 10);
const t = timetable({ file, schedule, calendar, group: "23-БА", from: "2026-10-19", range: "semester", today: "2026-10-19", session });
const on = (date) =>
t.days.find((d) => d.date === date).lessons
.filter((l) => ["Зачёт", "Экзамен", "Консультация"].includes(l.type))
.map((l) => [l.start, l.type, l.subject, l.subgroup]);
// Both subgroups' credits, at their own time and with their subgroup.
assert.deepEqual(on("2026-12-22"), [
["17:00", "Зачёт", "Рисунок", "Подгруппа 1"],
["17:00", "Зачёт", "Рисунок", "Подгруппа 2"],
]);
assert.deepEqual(on("2026-10-27"), [["17:00", "Консультация", "Введение в архитектурное проектирование", null]]);
// The span runs past the semester (3 January) to the last exam.
assert.equal(t.days.at(-1).date, "2027-01-15");
assert.deepEqual(on("2027-01-15"), [[lessons.at(-1).start, "Экзамен", "Бизнес-анализ", null]]);
assert.ok(t.subgroups.includes("Подгруппа 2"));
});