- 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.
116 lines
6.1 KiB
JavaScript
116 lines
6.1 KiB
JavaScript
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"));
|
||
});
|