import { test } from "node:test"; import assert from "node:assert/strict"; import { groupsIn, alternatives, entries, details, leadingTime } from "../server/parser.js"; import { roomMoves, weekPlan, onlyPlace } from "../server/rooms.js"; import { peopleAndRooms, roomsOn, teachersOn, movedOn } from "../server/directory.js"; // Cell formats found by the catalog's diagnostics (October 2026). test("Группы с направлением в скобках: 24-ИЯ (РА)и-1 … и-6 — разные группы", () => { assert.deepEqual(groupsIn("24-ИЯ (РА)и-1 | 24-ИЯ (РА)и-2 | 24-ИЯ (РА)и-3"), ["24-ИЯ (РА)и-1", "24-ИЯ (РА)и-2", "24-ИЯ (РА)и-3"]); assert.deepEqual(groupsIn("25 ИЯ (РА)и-1"), ["25-ИЯ (РА)и-1"]); assert.deepEqual(groupsIn("24-РГФи-1 26-ИТ-2"), ["24-РГФи-1", "26-ИТ-2"]); }); test("Перенос аудитории на дату, и с годом: «412н 26.10.2026-333»", () => { assert.deepEqual(roomMoves("Типология (л.) Полякова Г.В. 412н 26.10.2026-333", 2026).moves, [{ date: "2026-10-26", room: "333" }]); assert.deepEqual(roomMoves("Язык (п.з.) Горбач М.Н. 510н\n28.10.2026-504н", 2026).moves, [{ date: "2026-10-28", room: "504Н" }]); assert.deepEqual(roomMoves("ауд. 432 14.10. -ауд. 333", 2026).moves, [{ date: "2026-10-14", room: "333" }]); // The parser keeps the usual room; the move goes to that date only. const lesson = { raw: "Типология (л.) Полякова Г.В. 412н 26.10.2026-333", room: "412н", teacher: "Полякова Г.В." }; const entry = peopleAndRooms(lesson, "2026"); assert.deepEqual(roomsOn(entry, "2026-10-26"), ["333"]); assert.equal(movedOn(entry, "2026-10-26"), true); assert.deepEqual(roomsOn(entry, "2026-10-19"), ["412Н"]); assert.equal(movedOn(entry, "2026-10-19"), false); }); test("Строка с аудиторией по неделям продолжает занятие, а не становится новым", () => { const [one, ...rest] = entries("Язык профессионального общения (англ) (п.з.) Шабунина О.А.\n1-9н. 502н\nс 10н -359"); assert.equal(rest.length, 0); assert.equal(details(one.main).subject, "Язык профессионального общения (англ)"); assert.deepEqual(one.plan, [ { weeks: [[1, 9]], color: null, rooms: ["502Н"], teachers: [] }, { weeks: [[10, null]], color: null, rooms: ["359"], teachers: [] }, ]); const entry = peopleAndRooms({ raw: one.text, room: "", teacher: "Шабунина О.А.", plan: one.plan }, "2026"); assert.deepEqual(roomsOn(entry, "2026-10-20", { n: 8, color: "green" }), ["502Н"]); assert.deepEqual(roomsOn(entry, "2026-11-03", { n: 10, color: "green" }), ["359"]); }); test("Преподаватель по неделям и аудитория по цвету недели", () => { const teachers = weekPlan("2-9н.-Клос О.Ю., 10-14н.-Конторов В.Э. 319Д"); // The room at the end is the room of both parts. assert.deepEqual(teachers, [ { weeks: [[2, 9]], color: null, rooms: ["319Д"], teachers: ["Клос О.Ю."] }, { weeks: [[10, 14]], color: null, rooms: ["319Д"], teachers: ["Конторов В.Э."] }, ]); const entry = peopleAndRooms({ raw: "2-14н. История литературы (п.з.)\n2-9н.-Клос О.Ю., 10-14н.-Конторов В.Э. 319Д", room: "", teacher: "", plan: teachers }, "2026"); assert.deepEqual(teachersOn(entry, { n: 3, color: "white" }), ["Клос О.Ю."]); assert.deepEqual(teachersOn(entry, { n: 12, color: "green" }), ["Конторов В.Э."]); const [lesson] = entries("с 10н. Дискурсивная практика (рус) (п.з.) Булах А.С.\nбел.н.504н/зел.н.524н"); const byColor = peopleAndRooms({ raw: lesson.text, room: "", teacher: "", plan: lesson.plan }, "2026"); assert.deepEqual(roomsOn(byColor, "2026-11-10", { n: 11, color: "white" }), ["504Н"]); assert.deepEqual(roomsOn(byColor, "2026-11-17", { n: 12, color: "green" }), ["524Н"]); // The colour after each room (25-ДО): "ауд.533н-б.н.,420н-з.н.". const [after] = entries("Теоретические основы воспитания и развития речи детей раннего возраста (п.з.) Савейко В.Л. ауд.533н-б.н.,420н-з.н."); assert.deepEqual(after.plan, [ { weeks: null, color: "white", rooms: ["533Н"], teachers: ["Савейко В.Л."] }, { weeks: null, color: "green", rooms: ["420Н"], teachers: ["Савейко В.Л."] }, ]); const roomAfter = peopleAndRooms({ raw: after.text, room: "533н", teacher: "Савейко В.Л.", plan: after.plan }, "2026"); assert.deepEqual(roomsOn(roomAfter, "2026-10-30", { n: 9, color: "white" }), ["533Н"]); assert.deepEqual(roomsOn(roomAfter, "2026-10-23", { n: 8, color: "green" }), ["420Н"]); assert.deepEqual(weekPlan("ауд. 420н з.н., 533н б.н.").map((p) => [p.color, p.rooms]), [["white", ["533Н"]], ["green", ["420Н"]]]); assert.equal(onlyPlace("533н-б.н.,420н-з.н."), true); // "32н" before its colour is a room, not week 32. assert.deepEqual(weekPlan("Коррекционная дошкольная педагогика (л.) Остапчук С.В. ауд.64н -б.н., 32н-з.н.").map((p) => [p.color, p.rooms]), [["white", ["64Н"]], ["green", ["32Н"]]]); assert.equal(onlyPlace("1-9н. 502н"), true); assert.equal(onlyPlace("13,15 н. РБ (л.з) Сивацкий Д.С.209н"), false); }); test("Строка с видом и неделями без предмета — тот же предмет в другие недели", () => { const list = entries("2-6н. Потребительское право (л.) Савицкая К.Д. ауд. А1-06\n7,9,11н. – (п.з.) Савицкая К.Д. ауд. А1-06 8,12,14н. – (п.з.) Савицкая К.Д. ауд. А1-06"); assert.deepEqual(list.map((e) => [details(e.main).condition, details(e.main).subject, details(e.main).type]), [ ["2-6н.", "Потребительское право", "Лекция"], ["7,9,11н.", "Потребительское право", "Практика"], ["8,12,14н.", "Потребительское право", "Практика"], ]); assert.equal(details(list[0].main).room, "А1-06"); }); test("Дисциплины по выбору: заголовок, предмет перед неделями, виды по неделям", () => { const read = (raw) => entries(raw).map((e) => ((d) => `${d.condition} ${d.subject} · ${d.type}`)(details(e.main))); assert.deepEqual(read("Дисциплины по выбору: 1-16 н. Логика (л./п.з.) Магалинский И.В. ауд. С0-05\nСоциология 1-10 н. (л.) Шидловский С.О. 11-16 н. (п.з.) Лапацуева А.С.С0-03"), [ "1-16 н. Логика · Лекция и практика", "1-10 н. Социология · Лекция", "11-16 н. Социология · Практика", ]); assert.deepEqual(read("Дисциплина по выбору: Социология ауд. С0-03\n1-10 н. (л.) Шидловский С.О.\n11-18 н. (п.з.) Лапацуева А.С."), [ "1-10 н. Социология · Лекция", "11-18 н. Социология · Практика", ]); assert.deepEqual(read("Дисциплина сцециализации: 4-14 н. Преступления в сфере информационных технологий (л.) Чижик Е.В. ауд. В0-05"), [ "4-14 н. Преступления в сфере информационных технологий · Лекция", ]); assert.deepEqual(read("1-11н. - л., 12-14 – п.з. Защита населения Дмитриченко Н.С. ауд. 210м/к"), [ "1-11н. Защита населения · Лекция", "12-14н. Защита населения · Практика", ]); }); test("Время в начале ячейки: интервал с дефисом, заголовок со временем", () => { assert.deepEqual(leadingTime("17:00-17:20-информационный час-Клос О.Ю. 319Д").starts, ["17:00"]); assert.equal(leadingTime("17:00-17:20-информационный час").end, "17:20"); assert.deepEqual(alternatives("18.35+20.10 Новополоцк:\n1-5 н. Судебно-следственная психология (л.) Колесникова Е.В. ауд. 246"), [ "18.35+20.10 1-5 н. Судебно-следственная психология (л.) Колесникова Е.В. ауд. 246", ]); }); test("Подгруппа в тексте ячейки, точка вместо занятия, заголовок заглавными", () => { const own = details("12н.2п/гр. Экономика организаций (л.з.) Лисиченок Е.П. ауд.331"); assert.equal(own.subject, "Экономика организаций"); assert.equal(own.ownSubgroup, 2); assert.deepEqual(entries("."), []); assert.equal(details("ВВЕДЕНИЕ В ВЫСШЕЕ ОБРАЗОВАНИЕ\nг. Полоцк, Полоцкий коллегиум\n26-ГЕО, 26-ИТ-1 Ректорский вход\n26-ИТ-2, 26-СТ обзорная экскурсия").subject, "ВВЕДЕНИЕ В ВЫСШЕЕ ОБРАЗОВАНИЕ"); // A two-line subject stays whole. assert.equal(details("Интеллектуальная собственность - драйвер инновационного развития\nна пространстве Евразии").subject.includes("Евразии"), true); }); test("Старое имя разделённой группы: понятная ошибка, другое написание — та же группа", async () => { const { timetable } = await import("../server/timetable.js"); const schedule = { groups: ["24-ИЯ (РА)и-1", "24-ИЯ (РА)и-2"], lessons: [], notes: [], period: "Осенний семестр", source: {} }; const file = { id: "doc", name: "24-ИЯ (РА)и-1_24-ИЯ (РА)и-2.docx" }; assert.throws( () => timetable({ file, schedule, calendar: null, group: "24-ИЯ(РА)", from: "2026-10-19" }), (e) => e.code === "group_not_found" && /разделена: 24-ИЯ \(РА\)и-1, 24-ИЯ \(РА\)и-2/.test(e.message), ); assert.equal(timetable({ file, schedule, calendar: null, group: "24-ИЯ(РА)и-1", from: "2026-10-19" }).group, "24-ИЯ (РА)и-1"); });