Minor rfactor

This commit is contained in:
Ivymaster
2026-06-15 01:01:33 +02:00
parent 10a5aaf9b4
commit 245b724314
10 changed files with 1200 additions and 1012 deletions
File diff suppressed because it is too large Load Diff
@@ -1,8 +1,19 @@
import { waitForOk } from "../../../../support/helpers/network";
import { getDateForDay } from "../../../../support/helpers/date";
import { tempusPage } from "../../../../support/pages/tempus.po";
import { tempusApi } from "../../../../support/api/tempusApi";
const TARGETED_STUDY_PLAN_SHORT_CODE = "STG5";
const deleteMondayEvents = () =>
tempusApi
.getPlannerEvents(getDateForDay("monday"), getDateForDay("monday"))
.then((events) => {
events.forEach((event) => {
tempusApi.deleteKalenderEvent(event.kalender_id);
});
});
context("Tempus course picker tests", () => {
before(() => {
tempusPage.visitAndWaitForPlanner();
@@ -11,7 +22,6 @@ context("Tempus course picker tests", () => {
beforeEach(() => {
tempusPage.visitAndWaitForPlanner();
return tempusPage.clearMondayFirstColumnAfterTest();
});
it("can select one course and show preview of its events", () => {
@@ -51,14 +61,15 @@ context("Tempus course picker tests", () => {
});
it("can drag and drop one course event into the calendar", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
deleteMondayEvents();
tempusPage.getCalendarSection().should("exist");
tempusPage.waitForCalendarToFinishLoading();
tempusPage.selectCourseByName(TARGETED_STUDY_PLAN_SHORT_CODE);
waitForOk("@fetchCoursePickerCourses");
cy.wait(1000);
tempusPage.getCalendarEvents().then(($events) => {
cy.wrap($events.length).as("initialEventCount");
});
@@ -72,7 +83,6 @@ context("Tempus course picker tests", () => {
tempusPage.waitForCalendarToFinishLoading();
cy.get("@initialEventCount").then((initialEventCount) => {
console.log("Initial event count:", initialEventCount);
tempusPage
.getCalendarEvents()
.should("have.length", initialEventCount + 1);
@@ -1,25 +1,42 @@
import { waitForOk } from "../../../../support/helpers/network";
import { LEKTOR, STUDENT, tempusPage } from "../../../../support/pages/tempus.po";
import { getDateForDay } from "../../../../support/helpers/date";
import { tempusApi } from "../../../../support/api/tempusApi";
import {
LEKTOR,
STUDENT,
tempusPage,
} from "../../../../support/pages/tempus.po";
const TARGETED_STUDY_PLAN_SHORT_CODE = "STG5";
context("Tempus event mutation tests", () => {
beforeEach(() => {
tempusPage.visitAndWaitForPlanner();
return tempusPage.clearMondayFirstColumnAfterTest();
tempusApi
.getPlannerEvents(getDateForDay("monday"), getDateForDay("monday"))
.then((events) => {
events.forEach((event) => {
tempusApi.deleteKalenderEvent(event.kalender_id);
});
});
});
it("room change on planner preview updates planner event, but keeps original room on other previews", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.syncAndReloadPlanner();
tempusPage
.getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime("Wednesday", "08:00:00")
.getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime(
"Wednesday",
"08:00:00",
)
.should("have.length.greaterThan", 0);
tempusPage
.getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime("Wednesday", "08:00:00")
.getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime(
"Wednesday",
"08:00:00",
)
.first()
.invoke("attr", "data-fhc-draggable-value")
.then((eventJSON) => {
@@ -29,8 +46,8 @@ context("Tempus event mutation tests", () => {
const eventId = eventData?.id;
const originalRoom = eventData?.orig?.ort_kurzbz;
expect(eventId, "planner event id").to.exist;
expect(originalRoom, "original event room").to.be.a("string").and.not
.be.empty;
expect(originalRoom, "original event room").to.be.a("string").and.not.be
.empty;
tempusPage.expectCalendarEventRoom(eventId, originalRoom);
@@ -83,16 +100,20 @@ context("Tempus event mutation tests", () => {
});
it("sync after planner preview room change loads new room on all previews", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.syncAndReloadPlanner();
tempusPage
.getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime("Wednesday", "08:00:00")
.getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime(
"Wednesday",
"08:00:00",
)
.should("have.length.greaterThan", 0);
tempusPage
.getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime("Wednesday", "08:00:00")
.getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime(
"Wednesday",
"08:00:00",
)
.first()
.invoke("attr", "data-fhc-draggable-value")
.then((eventJSON) => {
@@ -102,8 +123,8 @@ context("Tempus event mutation tests", () => {
const eventId = eventData?.id;
const originalRoom = eventData?.orig?.ort_kurzbz;
expect(eventId, "planner event id").to.exist;
expect(originalRoom, "original event room").to.be.a("string").and.not
.be.empty;
expect(originalRoom, "original event room").to.be.a("string").and.not.be
.empty;
tempusPage
.getCalendarEventById(eventId)
@@ -170,8 +191,6 @@ context("Tempus event mutation tests", () => {
});
it("event deletion on planner preview preservers event on planner, but shows it as unsynced on lektor and student preview", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.syncAndReloadPlanner();
tempusPage
@@ -205,8 +224,6 @@ context("Tempus event mutation tests", () => {
});
it("syncing event deletion on planner preview removes event from other previews", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.syncAndReloadPlanner();
tempusPage
@@ -240,8 +257,6 @@ context("Tempus event mutation tests", () => {
});
it("can bottom resize an event on planner preview", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.getCalendarSection().should("exist");
tempusPage.waitForCalendarToFinishLoading();
@@ -273,11 +288,11 @@ context("Tempus event mutation tests", () => {
.realMouseUp();
cy.wait("@updateCalendarEvent").then((interception) => {
console.log("updatedEventId", interception.response.body);
expect(interception.response.statusCode).to.eq(200);
expect(interception.response.body.data.retval).to.exist;
const updatedEventId =
interception.response.body.data.retval?.kalender_id ?? interception.response.body.data.retval;
interception.response.body.data.retval?.kalender_id ??
interception.response.body.data.retval;
cy.wrap(updatedEventId).as("updatedEventId");
});
waitForOk("@fetchPlanData");
@@ -302,8 +317,6 @@ context("Tempus event mutation tests", () => {
});
it.skip("can top resize an event on planner preview", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.getCalendarSection().should("exist");
tempusPage.waitForCalendarToFinishLoading();
@@ -335,12 +348,12 @@ context("Tempus event mutation tests", () => {
.realMouseUp();
cy.wait("@updateCalendarEvent").then((interception) => {
console.log("updatedEventId top", interception.response.body);
expect(interception.response.statusCode).to.eq(200);
expect(interception.response.body.data.retval).to.exist;
const updatedEventId =
interception.response.body.data.retval?.kalender_id ?? interception.response.body.data.retval;
interception.response.body.data.retval?.kalender_id ??
interception.response.body.data.retval;
cy.wrap(updatedEventId).as("updatedEventId");
});
@@ -366,21 +379,35 @@ context("Tempus event mutation tests", () => {
});
it.skip("can drag and drop one course event into the calendar when Stundenraster is disabled", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.setCurrentSemester();
tempusPage.getCalendarSection().should("exist");
tempusPage.waitForCalendarToFinishLoading();
tempusPage.disableStundenraster();
tempusPage.waitForCalendarToFinishLoading();
tempusPage.selectCourseByName(TARGETED_STUDY_PLAN_SHORT_CODE);
waitForOk("@fetchCoursePickerCourses");
cy.wait(1000);
tempusPage.getCalendarEvents().then(($events) => {
cy.wrap($events.length).as("initialEventCount");
});
tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0);
tempusPage.dropCourseOnCalendarPart(0, 1);
tempusPage
.getCoursePickerRows()
.first()
.scrollIntoView()
.should("be.visible")
.drag(".fhc-calendar-base-grid-line:first", {
waitForAnimations: true,
animationDistanceThreshold: 0,
target: { position: "top" },
});
waitForOk("@addCalendarEvent");
waitForOk("@fetchPlanData");
@@ -395,13 +422,15 @@ context("Tempus event mutation tests", () => {
});
it.skip("can drag and drop an existing event when Stundenraster is disabled", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.syncAndReloadPlanner();
tempusPage.waitForCalendarToFinishLoading();
tempusPage.disableStundenraster();
cy.wait(1000);
tempusPage
.getCalendarEventsByTimeRange("08:00:00", "08:45:00")
.getCalendarEventsByWeekdayAndStartTime("Wednesday", "11:20:00")
.first()
.invoke("attr", "data-fhc-draggable-value")
.then((eventJSON) => {
@@ -410,10 +439,14 @@ context("Tempus event mutation tests", () => {
const eventId = JSON.parse(eventJSON)?.id;
expect(eventId).to.exist;
tempusPage.getEventGridRow(eventId).as("originalGridRow");
tempusPage.dropEventOnCalendarPart(eventId, 6, {
scrollIntoView: true,
});
tempusPage
.getCalendarEventById(eventId)
.should("be.visible")
.drag(".fhc-calendar-base-grid-line:first", {
waitForAnimations: true,
animationDistanceThreshold: 0,
target: { position: "top" },
});
cy.wait("@updateCalendarEvent").then((interception) => {
expect(interception.response.statusCode).to.eq(200);
@@ -429,11 +462,14 @@ context("Tempus event mutation tests", () => {
cy.get("@updatedEventId").then((updatedEventId) => {
tempusPage.getCalendarEventById(updatedEventId).should("be.visible");
cy.get("@originalGridRow").then((originalGridRow) => {
tempusPage.getEventGridRow(updatedEventId).then((updatedGridRow) => {
expect(updatedGridRow).to.not.eq(originalGridRow);
tempusPage
.getCalendarEventsByWeekday("Monday")
.first()
.invoke("attr", "data-fhc-draggable-value")
.then((movedEventJSON) => {
const movedEventId = JSON.parse(movedEventJSON)?.id;
expect(movedEventId).to.eq(updatedEventId);
});
});
});
});
});
@@ -89,14 +89,10 @@ context("Tempus filter tests", () => {
waitForOk("@fetchPlanData");
tempusPage.waitForCalendarToFinishLoading();
tempusPage
.getSelectedRoomIndicator(selectedRoom)
.should("be.visible");
tempusPage.getSelectedRoomIndicator(selectedRoom).should("be.visible");
tempusPage.getCalendarEvents().should("have.length.greaterThan", 0);
tempusPage
.getSelectedRoomRemoveButton(selectedRoom)
.click();
tempusPage.getSelectedRoomRemoveButton(selectedRoom).click();
waitForOk("@fetchPlanData");
tempusPage.waitForCalendarToFinishLoading();
@@ -119,7 +115,9 @@ context("Tempus filter tests", () => {
.then((eventJSON) => {
expect(eventJSON).to.exist;
const selectedLecturer = tempusPage.getFirstLecturer(JSON.parse(eventJSON));
const selectedLecturer = tempusPage.getFirstLecturer(
JSON.parse(eventJSON),
);
expect(selectedLecturer?.kurzbz, "first event lecturer").to.exist;
const lecturerSearch =
@@ -183,7 +181,9 @@ context("Tempus filter tests", () => {
.then((eventJSON) => {
expect(eventJSON).to.exist;
const selectedLecturer = tempusPage.getFirstLecturer(JSON.parse(eventJSON));
const selectedLecturer = tempusPage.getFirstLecturer(
JSON.parse(eventJSON),
);
expect(selectedLecturer?.kurzbz, "first event lecturer").to.exist;
const lecturerSearch =
@@ -247,7 +247,9 @@ context("Tempus filter tests", () => {
.then((eventJSON) => {
expect(eventJSON).to.exist;
const selectedLecturer = tempusPage.getFirstLecturer(JSON.parse(eventJSON));
const selectedLecturer = tempusPage.getFirstLecturer(
JSON.parse(eventJSON),
);
expect(selectedLecturer?.kurzbz, "first event lecturer").to.exist;
const lecturerSearch =
@@ -296,7 +298,9 @@ context("Tempus filter tests", () => {
.then((eventJSON) => {
expect(eventJSON).to.exist;
const selectedLecturer = tempusPage.getFirstLecturer(JSON.parse(eventJSON));
const selectedLecturer = tempusPage.getFirstLecturer(
JSON.parse(eventJSON),
);
expect(selectedLecturer?.kurzbz, "first event lecturer").to.exist;
const lecturerSearch =
@@ -330,15 +334,15 @@ context("Tempus filter tests", () => {
.getSelectedLecturerWishToggle(selectedLecturerName)
.should("be.visible")
.click();
tempusPage
.getLecturerWishOverlays()
.should("have.length", 0)
tempusPage.getLecturerWishOverlays().should("have.length", 0);
tempusPage
.getSelectedLecturerWishToggle(selectedLecturerName)
.should("be.visible")
.click();
tempusPage.getLecturerWishOverlays().should("have.length.greaterThan", 0);
tempusPage
.getLecturerWishOverlays()
.should("have.length.greaterThan", 0);
});
});
});
@@ -1,4 +1,6 @@
import { waitForOk } from "../../../../support/helpers/network";
import { getDateForDay } from "../../../../support/helpers/date";
import { tempusApi } from "../../../../support/api/tempusApi";
import {
LEKTOR,
PLANER,
@@ -9,7 +11,14 @@ import {
context("Tempus role preview tests", () => {
beforeEach(() => {
tempusPage.visitAndWaitForPlanner();
return tempusPage.clearMondayFirstColumnAfterTest();
tempusApi
.getPlannerEvents(getDateForDay("monday"), getDateForDay("monday"))
.then((events) => {
events.forEach((event) => {
tempusApi.deleteKalenderEvent(event.kalender_id);
});
});
});
it("shows the same number of calendar events for all role previews", () => {
@@ -36,8 +45,6 @@ context("Tempus role preview tests", () => {
});
it("on planner event change shows unchanged event on other roles", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.syncAndReloadPlanner();
tempusPage
@@ -89,8 +96,6 @@ context("Tempus role preview tests", () => {
});
it("sync after planner preview event change loads event in same way on all previews", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.syncAndReloadPlanner();
tempusPage
@@ -142,8 +147,6 @@ context("Tempus role preview tests", () => {
});
it.skip("live unlock after planner preview event change loads event in same way on planner and lektor, but not on student preview", () => {
tempusPage.clearMondayFirstColumnBeforeAndAfter();
tempusPage.syncAndReloadPlanner();
tempusPage
@@ -5,7 +5,7 @@ context("Tempus smoke tests", () => {
beforeEach(() => {
tempusPage.visitAndWaitForPlanner();
});
it("can access Tempus page with valid credentials", () => {
tempusPage.getTempusOverview().should("be.visible");
});
@@ -75,13 +75,10 @@ context("Tempus smoke tests", () => {
tempusPage
.getReservationDragHandle()
.scrollIntoView()
.drag(
tempusPage.selectors.calendarBaseGrid,
{
waitForAnimations: false,
animationDistanceThreshold: 0,
},
);
.drag(tempusPage.selectors.calendarBaseGrid, {
waitForAnimations: false,
animationDistanceThreshold: 0,
});
tempusPage.getReservationModal().should("be.visible");
});
@@ -0,0 +1,105 @@
const KALENDER_API = "/index.ci.php/api/frontend/v1/tempus";
export const tempusApi = {
getStudyPlansTree: () =>
cy
.request({
method: "GET",
url: `/index.ci.php/api/frontend/v1/lv/StgTree`,
})
.then((response) => {
expect(response.status).to.eq(200);
expect(response.body).to.have.nested.property("meta.status", "success");
expect(response.body.data).to.be.an("array");
return response.body.data;
}),
getCoursesByStudyPlan: (studyPlanId, semesterShortCode) =>
cy
.request({
method: "GET",
url: `/index.ci.php/api/frontend/v1/tempus/coursepicker/getByStg`,
qs: {
stg: studyPlanId,
studiensemester_kurzbz: semesterShortCode,
},
})
.then((response) => {
expect(response.status).to.eq(200);
expect(response.body).to.have.nested.property("meta.status", "success");
expect(response.body.data).to.be.an("array");
return response.body.data;
}),
getPlannerEvents: (startDate, endDate) =>
cy
.request({
method: "GET",
url: `${KALENDER_API}/Kalender/getPlan`,
qs: {
start_date: startDate,
end_date: endDate,
},
})
.then((response) => {
expect(response.status).to.eq(200);
expect(response.body).to.have.nested.property("meta.status", "success");
expect(response.body.data).to.be.an("array");
return response.body.data;
}),
createKalenderEvent: (lehreinheitId, startDateTime, endDateTime) =>
cy.request({
method: "POST",
url: `${KALENDER_API}/Kalender/addKalenderEvent`,
body: {
lehreinheit_id: lehreinheitId,
start_date: startDateTime,
end_date: endDateTime,
},
failOnStatusCode: false,
}),
updateKalenderEvent: (kalenderId, startDateTime, endDateTime) =>
cy.request({
method: "POST",
url: `${KALENDER_API}/Kalender/updateKalenderEvent`,
form: true,
body: {
kalender_id: kalenderId,
"updatedInfos[start_time]": startDateTime,
"updatedInfos[end_time]": endDateTime,
},
failOnStatusCode: false,
}),
deleteKalenderEvent: (kalenderId) =>
cy.request({
method: "POST",
url: `${KALENDER_API}/Kalender/deleteEntry`,
form: true,
body: {
kalender_id: kalenderId,
},
failOnStatusCode: false,
}),
getSettingsData: () => ({
ignore_kollision: false,
kollision_student: false,
ignore_reservierung: false,
ignore_zeitsperre: false,
}),
updateSettingsData: (options) =>
cy.request({
method: "POST",
url: `${KALENDER_API}/config/set`,
failOnStatusCode: false,
headers: { "Cache-Control": "no-cache", Pragma: "no-cache" },
body: { ...options },
}),
};
@@ -0,0 +1,21 @@
import moment from "moment";
export function getDateForDay(dayName) {
const days = {
monday: 0,
tuesday: 1,
wednesday: 2,
thursday: 3,
friday: 4,
saturday: 5,
sunday: 6,
};
const dayIndex = days[dayName.toLowerCase()];
if (dayIndex === undefined) {
throw new Error(`Invalid day name: ${dayName}`);
}
return moment().startOf("isoWeek").add(dayIndex, "days").format("YYYY-MM-DD");
}
+292 -301
View File
@@ -6,13 +6,13 @@ export const STUDENT = "Student";
export const SYNC = "Sync";
const weekdayToGridRowMap = {
"Monday": "1",
"Tuesday": "2",
"Wednesday": "3",
"Thursday": "4",
"Friday": "5",
"Saturday": "6",
"Sunday": "7",
Monday: "1",
Tuesday: "2",
Wednesday: "3",
Thursday: "4",
Friday: "5",
Saturday: "6",
Sunday: "7",
};
const roleFetchAliases = {
[PLANER]: "@fetchPlanData",
@@ -26,230 +26,14 @@ class TempusPage {
parkingSlot: "div[data-cy='tempus'] #parkingslot",
};
cleanupMondayFirstColumnAfterTest = false;
visit = () => cy.visit("/index.ci.php/tempus", {
onBeforeLoad(win) {
win.localStorage.removeItem("tempus_parking");
win.localStorage.removeItem("tempus_searchtypes");
win.sessionStorage.removeItem("tempus_searchstr");
},
});
getTempusOverview = () => cy.get("div[data-cy='tempus']");
getSlideInCoursesMenu = () => cy.get("div[data-cy='verbandMenu']");
getSidebarMenu = () => this.getTempusOverview().find("#sidebarMenu");
getCalendarSection = () => this.getTempusOverview().find(".fhc-calendar-base");
getCalendarBaseGrid = () => this.getCalendarSection().find(".fhc-calendar-base-grid");
getCoursePicker = () => this.getTempusOverview().find(".course-picker");
getPreviewRoleOptionsHolder = () => this.getTempusOverview().find("[data-cy='previewRoleOptionsHolder']");
getAllCoursesSliderBtn = () => this.getSidebarMenu().find("button[title='Verband']").first();
getCourseTreeRows = () => this.getSlideInCoursesMenu().find(".p-treetable-tbody tr");
getCoursePickerRows = () => this.getCoursePicker().find(".course-picker-row");
getCoursePickerSearchInput = () => this.getCoursePicker().find("input");
getCalendarEventsPerWeekHolders = () => this.getCalendarBaseGrid().find(".fhc-calendar-base-grid-line");
getCalendarEvents = () => this.getCalendarSection().then(($calendar) => {
return $calendar.find(".fhc-calendar-base-grid-line-event");
});
getLecturerWishOverlays = () => this.getCalendarSection().find(".bg-lecturer-wish");
getCalendarEventsWithRoom = () => this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
return !!eventData?.orig?.ort_kurzbz;
});
getCalendarEventsWithLecturer = () => this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
return eventData?.orig?.lektor?.some((lecturer) => lecturer?.kurzbz);
});
getCalendarEventsWithLehreinheit = () => this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
const lehreinheitIds = eventData?.orig?.lehreinheit_id;
return Array.isArray(lehreinheitIds)
? lehreinheitIds.length > 0
: !!lehreinheitIds;
});
getCalendarEventsWithLehreinheitAndRoom = () =>
this.getCalendarEventsWithLehreinheit().filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
return !!eventData?.orig?.ort_kurzbz;
});
getNavbarSearchInput = () => this.getTempusOverview().find("header .searchbar_input");
getNavbarRoomSearchResult = (room) => {
const escapedRoom = room.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
return cy.contains(
".searchbar-result-room .searchbar-result-template-action",
new RegExp(`^\\s*${escapedRoom}\\s*$`),
);
};
getFirstNavbarEmployeeSearchResult = () =>
cy.get(".searchbar-result-student .searchbar_data .searchbar-result-template-action").first();
getSelectedRoomIndicator = (room) =>
this.getSidebarMenu().contains(".room-selection", room);
getSelectedRoomRemoveButton = (room) =>
this.getSelectedRoomIndicator(room).find("i.fa-xmark");
getSelectedLecturerIndicator = (lecturer) =>
this.getSidebarMenu().contains(".lecture-selection .fw-semibold", lecturer);
getSelectedLecturerRemoveButton = (lecturer) =>
this.getSelectedLecturerIndicator(lecturer).find("i.fa-xmark");
getSelectedLecturerPlanToggle = (lecturer) =>
this.getSelectedLecturerIndicator(lecturer)
.parent()
.contains(".d-flex.align-items-center", "Plan");
getSelectedLecturerWishToggle = (lecturer) =>
this.getSelectedLecturerIndicator(lecturer)
.parent()
.contains(".d-flex.align-items-center", "Zeitwünsche");
getCalendarEventById = (id) => this.getCalendarEvents().filter(`div[data-id="event-${id}"]`);
getCalendarPartDropTarget = (partIndex) =>
`${this.selectors.calendarBaseGrid} .part-body:nth-of-type(${partIndex})`;
dropEventOnCalendarPart = (eventId, partIndex, options = {}) => {
const event = this.getCalendarEventById(eventId);
return event.should("be.visible").drag(
this.getCalendarPartDropTarget(partIndex),
{
waitForAnimations: false,
animationDistanceThreshold: 0,
...options,
visit = () =>
cy.visit("/index.ci.php/tempus", {
onBeforeLoad(win) {
win.localStorage.removeItem("tempus_parking");
win.localStorage.removeItem("tempus_searchtypes");
win.sessionStorage.removeItem("tempus_searchstr");
},
);
};
dropCourseOnCalendarPart = (courseIndex, partIndex, options = {}) => {
const course = this.getCoursePickerRows().eq(courseIndex).find("span").first();
return course.should("be.visible").drag(
this.getCalendarPartDropTarget(partIndex),
{
waitForAnimations: false,
animationDistanceThreshold: 0,
scrollBehavior: "top",
...options,
},
);
};
getEventGridRowFromStyle = (style) =>
/grid-row:\s*([^;]+)/.exec(style)?.[1]?.trim();
getEventGridRow = (id) =>
this.getCalendarEventById(id)
.invoke("attr", "style")
.then((style) => this.getEventGridRowFromStyle(style));
getCalendarEventsByWeekday = (weekday) => this.getCalendarEventsPerWeekHolders().eq(weekdayToGridRowMap[weekday] - 1).find(".fhc-calendar-base-grid-line-event");
getCalendarEventsByStartTime = (startTime) => this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
return eventData?.orig?.beginn === startTime;
});
getCalendarEventsByTimeRange = (startTime, endTime) => this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
return eventData?.orig?.beginn === startTime && eventData?.orig?.ende === endTime;
});
getCalendarEventsBySoftTimeRange = (startTime, endTime) => this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
return eventData?.orig?.beginn >= startTime && eventData?.orig?.ende <= endTime;
});
getCalendarEventsByWeekdayAndStartTime = (weekday, startTime) => this.getCalendarEventsByWeekday(weekday).filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
return eventData?.orig?.beginn === startTime;
});
getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime = (weekday, startTime) =>
this.getCalendarEventsByWeekdayAndStartTime(weekday, startTime).filter((index, event) => {
const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value"));
return !!eventData?.orig?.ort_kurzbz && eventData?.orig?.lehreinheit_id;
});
getKalenderId = (eventData) =>
eventData?.orig?.kalender_id ?? eventData?.id;
getCalendarEventData = ($event) =>
JSON.parse($event.attr("data-fhc-draggable-value"));
getFirstLecturer = (eventData) =>
eventData?.orig?.lektor?.find((lecturer) => lecturer?.kurzbz);
getUpdatedKalenderId = (interception) => {
const retval = interception.response.body?.data?.retval;
return retval?.kalender_id ?? retval;
};
getMondayFirstColumnEventData = ($body) =>
[
...$body
.find(`${this.selectors.calendarBaseGrid} .fhc-calendar-base-grid-line`)
.first()
.find(".fhc-calendar-base-grid-line-event"),
]
.map((event) => {
const eventJSON = event.getAttribute("data-fhc-draggable-value");
try {
return eventJSON ? JSON.parse(eventJSON) : null;
} catch {
return null;
}
})
.filter(Boolean);
getCalendarLoadingEvents = () => this.getCalendarSection().find(".placeholder-glow");
getCalendarEventModal = () => this.getCalendarSection().find(".bootstrap-modal.show");
getEventContextMenu = () => cy.get("[data-cy='eventContextMenu']");
getEventContextMenuOption = (option) => this.getEventContextMenu().contains("button", option);
getRaumauswahlModal = () =>
cy.contains(".bootstrap-modal.show .modal-title", "Raumauswahl")
.closest(".bootstrap-modal.show");
getRaumauswahlRoomOptions = () =>
this.getRaumauswahlModal().find(".list-group-item");
getCalendarEventRoom = (id) =>
this.getCalendarEventById(id).find(".event-place");
getStundenrasterToggle = () =>
cy.contains(".form-check-label", "Stundenraster").parent();
getHistoryModal = () => cy.get("[data-cy='historyModal']");
getReservationDragHandle = () => cy.get("[data-cy='reservationDragHandle']");
getReservationModal = () => cy.get("[data-cy='reservationModal']");
getEventParkingSlot = () => this.getSidebarMenu().find("#parkingslot");
getParkedEvents = () => this.getEventParkingSlot().find(".fhc-calendar-base-grid-line-event");
getPreviewRoleButton = (role) => this.getPreviewRoleOptionsHolder().contains("button", role);
getSemesterSetterButton = () => this.getSidebarMenu().find(".stv-studiensemester button").last();
openCoursesMenu = () => {
this.getAllCoursesSliderBtn().click();
this.getSlideInCoursesMenu().should("be.visible");
};
selectFirstCourse = () => {
this.openCoursesMenu();
this.getCourseTreeRows().first().click();
};
selectCourseByName = (courseShortName) => {
this.openCoursesMenu();
this.getCourseTreeRows().contains(courseShortName).click();
}
selectPreviewRole = (role) => {
this.getPreviewRoleButton(role).click();
};
syncAndReloadPlanner = () => {
this.selectPreviewRole(SYNC);
waitForOk("@syncCalendar");
waitForOk("@fetchPlanData");
this.waitForCalendarToFinishLoading();
};
selectRoleAndWait = (role) => {
this.selectPreviewRole(role);
waitForOk(roleFetchAliases[role]);
this.waitForCalendarToFinishLoading();
};
expectCalendarEventRoom = (eventId, expectedRoom) => {
this.getCalendarEventRoom(eventId)
.scrollIntoView()
.should("be.visible")
.invoke("text")
.then((roomText) => {
expect(roomText.trim(), "event room").to.eq(expectedRoom);
});
};
setupIntercepts = () => {
cy.intercept({ method: "GET", url: "**/StgTree" }).as("fetchCourseTree");
@@ -310,6 +94,254 @@ class TempusPage {
}).as("deleteCalendarEvent");
};
getTempusOverview = () => cy.get("div[data-cy='tempus']");
getSlideInCoursesMenu = () => cy.get("div[data-cy='verbandMenu']");
getSidebarMenu = () => this.getTempusOverview().find("#sidebarMenu");
getCalendarSection = () =>
this.getTempusOverview().find(".fhc-calendar-base");
getCalendarBaseGrid = () =>
this.getCalendarSection().find(".fhc-calendar-base-grid");
getCoursePicker = () => this.getTempusOverview().find(".course-picker");
getPreviewRoleOptionsHolder = () =>
this.getTempusOverview().find("[data-cy='previewRoleOptionsHolder']");
getAllCoursesSliderBtn = () =>
this.getSidebarMenu().find("button[title='Verband']").first();
getCourseTreeRows = () =>
this.getSlideInCoursesMenu().find(".p-treetable-tbody tr");
getCoursePickerRows = () => this.getCoursePicker().find(".course-picker-row");
getCoursePickerSearchInput = () => this.getCoursePicker().find("input");
getCalendarEventsPerWeekHolders = () =>
this.getCalendarBaseGrid().find(".fhc-calendar-base-grid-line");
getCalendarEvents = () =>
this.getCalendarSection().then(($calendar) => {
return $calendar.find(".fhc-calendar-base-grid-line-event");
});
getLecturerWishOverlays = () =>
this.getCalendarSection().find(".bg-lecturer-wish");
getCalendarEventsWithRoom = () =>
this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
return !!eventData?.orig?.ort_kurzbz;
});
getCalendarEventsWithLecturer = () =>
this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
return eventData?.orig?.lektor?.some((lecturer) => lecturer?.kurzbz);
});
getCalendarEventsWithLehreinheit = () =>
this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
const lehreinheitIds = eventData?.orig?.lehreinheit_id;
return Array.isArray(lehreinheitIds)
? lehreinheitIds.length > 0
: !!lehreinheitIds;
});
getCalendarEventsWithLehreinheitAndRoom = () =>
this.getCalendarEventsWithLehreinheit().filter((index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
return !!eventData?.orig?.ort_kurzbz;
});
getNavbarSearchInput = () =>
this.getTempusOverview().find("header .searchbar_input");
getNavbarRoomSearchResult = (room) => {
const escapedRoom = room.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
return cy.contains(
".searchbar-result-room .searchbar-result-template-action",
new RegExp(`^\\s*${escapedRoom}\\s*$`),
);
};
getFirstNavbarEmployeeSearchResult = () =>
cy
.get(
".searchbar-result-student .searchbar_data .searchbar-result-template-action",
)
.first();
getSelectedRoomIndicator = (room) =>
this.getSidebarMenu().contains(".room-selection", room);
getSelectedRoomRemoveButton = (room) =>
this.getSelectedRoomIndicator(room).find("i.fa-xmark");
getSelectedLecturerIndicator = (lecturer) =>
this.getSidebarMenu().contains(".lecture-selection .fw-semibold", lecturer);
getSelectedLecturerRemoveButton = (lecturer) =>
this.getSelectedLecturerIndicator(lecturer).find("i.fa-xmark");
getSelectedLecturerPlanToggle = (lecturer) =>
this.getSelectedLecturerIndicator(lecturer)
.parent()
.contains(".d-flex.align-items-center", "Plan");
getSelectedLecturerWishToggle = (lecturer) =>
this.getSelectedLecturerIndicator(lecturer)
.parent()
.contains(".d-flex.align-items-center", "Zeitwünsche");
getCalendarEventById = (id) =>
this.getCalendarEvents().filter(`div[data-id="event-${id}"]`);
getCalendarPartDropTarget = (partIndex) =>
`${this.selectors.calendarBaseGrid} .part-body:nth-of-type(${partIndex})`;
getEventGridRowFromStyle = (style) =>
/grid-row:\s*([^;]+)/.exec(style)?.[1]?.trim();
getEventGridRow = (id) =>
this.getCalendarEventById(id)
.invoke("attr", "style")
.then((style) => this.getEventGridRowFromStyle(style));
getCalendarEventsByWeekday = (weekday) =>
this.getCalendarEventsPerWeekHolders()
.eq(weekdayToGridRowMap[weekday] - 1)
.find(".fhc-calendar-base-grid-line-event");
getCalendarEventsByStartTime = (startTime) =>
this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
return eventData?.orig?.beginn === startTime;
});
getCalendarEventsByTimeRange = (startTime, endTime) =>
this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
return (
eventData?.orig?.beginn === startTime &&
eventData?.orig?.ende === endTime
);
});
getCalendarEventsBySoftTimeRange = (startTime, endTime) =>
this.getCalendarEvents().filter((index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
return (
eventData?.orig?.beginn >= startTime && eventData?.orig?.ende <= endTime
);
});
getCalendarEventsByWeekdayAndStartTime = (weekday, startTime) =>
this.getCalendarEventsByWeekday(weekday).filter((index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
return eventData?.orig?.beginn === startTime;
});
getCalendarEventsWithLehreinheitAndRoomByWeekdayAndStartTime = (
weekday,
startTime,
) =>
this.getCalendarEventsByWeekdayAndStartTime(weekday, startTime).filter(
(index, event) => {
const eventData = JSON.parse(
event.getAttribute("data-fhc-draggable-value"),
);
return !!eventData?.orig?.ort_kurzbz && eventData?.orig?.lehreinheit_id;
},
);
getKalenderId = (eventData) => eventData?.orig?.kalender_id ?? eventData?.id;
getCalendarEventData = ($event) =>
JSON.parse($event.attr("data-fhc-draggable-value"));
getFirstLecturer = (eventData) =>
eventData?.orig?.lektor?.find((lecturer) => lecturer?.kurzbz);
getUpdatedKalenderId = (interception) => {
const retval = interception.response.body?.data?.retval;
return retval?.kalender_id ?? retval;
};
getMondayFirstColumnEventData = ($body) =>
[
...$body
.find(`${this.selectors.calendarBaseGrid} .fhc-calendar-base-grid-line`)
.first()
.find(".fhc-calendar-base-grid-line-event"),
]
.map((event) => {
const eventJSON = event.getAttribute("data-fhc-draggable-value");
try {
return eventJSON ? JSON.parse(eventJSON) : null;
} catch {
return null;
}
})
.filter(Boolean);
getCalendarLoadingEvents = () =>
this.getCalendarSection().find(".placeholder-glow");
getCalendarEventModal = () =>
this.getCalendarSection().find(".bootstrap-modal.show");
getEventContextMenu = () => cy.get("[data-cy='eventContextMenu']");
getEventContextMenuOption = (option) =>
this.getEventContextMenu().contains("button", option);
getRaumauswahlModal = () =>
cy
.contains(".bootstrap-modal.show .modal-title", "Raumauswahl")
.closest(".bootstrap-modal.show");
getRaumauswahlRoomOptions = () =>
this.getRaumauswahlModal().find(".list-group-item");
getCalendarEventRoom = (id) =>
this.getCalendarEventById(id).find(".event-place");
getStundenrasterToggle = () =>
cy.contains(".form-check-label", "Stundenraster").parent();
getHistoryModal = () => cy.get("[data-cy='historyModal']");
getReservationDragHandle = () => cy.get("[data-cy='reservationDragHandle']");
getReservationModal = () => cy.get("[data-cy='reservationModal']");
getEventParkingSlot = () => this.getSidebarMenu().find("#parkingslot");
getParkedEvents = () =>
this.getEventParkingSlot().find(".fhc-calendar-base-grid-line-event");
getPreviewRoleButton = (role) =>
this.getPreviewRoleOptionsHolder().contains("button", role);
getSemesterSetterButton = () =>
this.getSidebarMenu().find(".stv-studiensemester button").last();
openCoursesMenu = () => {
this.getAllCoursesSliderBtn().click();
this.getSlideInCoursesMenu().should("be.visible");
};
selectFirstCourse = () => {
this.openCoursesMenu();
this.getCourseTreeRows().first().click();
};
selectCourseByName = (courseShortName) => {
this.openCoursesMenu();
this.getCourseTreeRows().contains(courseShortName).click();
};
selectPreviewRole = (role) => {
this.getPreviewRoleButton(role).click();
};
syncAndReloadPlanner = () => {
this.selectPreviewRole(SYNC);
waitForOk("@syncCalendar");
waitForOk("@fetchPlanData");
this.waitForCalendarToFinishLoading();
};
selectRoleAndWait = (role) => {
this.selectPreviewRole(role);
waitForOk(roleFetchAliases[role]);
this.waitForCalendarToFinishLoading();
};
expectCalendarEventRoom = (eventId, expectedRoom) => {
this.getCalendarEventRoom(eventId)
.scrollIntoView()
.should("be.visible")
.invoke("text")
.then((roomText) => {
expect(roomText.trim(), "event room").to.eq(expectedRoom);
});
};
visitAndWaitForPlanner = () => {
cy.login();
this.setupIntercepts();
@@ -320,74 +352,6 @@ class TempusPage {
this.waitForCalendarToFinishLoading();
};
reloadPlannerForCleanup = () => {
this.visit();
waitForOk("@fetchCourseTree");
waitForOk("@fetchPlanData");
return this.waitForCalendarToFinishLoading();
};
deleteKalenderEvent = (kalenderId) =>
cy.request({
method: "POST",
url: "/index.ci.php/api/frontend/v1/tempus/Kalender/deleteEntry",
form: true,
body: {
kalender_id: kalenderId,
},
failOnStatusCode: false,
});
clearMondayFirstColumn = () => {
this.reloadPlannerForCleanup();
return cy.get("body").then(($body) => {
const calendarIds = [
...new Set(
this.getMondayFirstColumnEventData($body)
.map(this.getKalenderId)
.filter(Boolean),
),
];
if (!calendarIds.length) {
return;
}
return cy
.wrap(calendarIds, { log: false })
.each((kalenderId) => {
return this.deleteKalenderEvent(kalenderId).then((response) => {
if (response.status !== 200) {
Cypress.log({
name: "tempus cleanup",
message: `Could not delete calendar event ${kalenderId}: ${response.status}`,
});
}
});
})
.then(() => {
return this.reloadPlannerForCleanup();
});
});
};
clearMondayFirstColumnBeforeAndAfter = () => {
this.cleanupMondayFirstColumnAfterTest = true;
return this.clearMondayFirstColumn();
};
clearMondayFirstColumnAfterTest = () => {
const shouldClearMondayFirstColumn = this.cleanupMondayFirstColumnAfterTest;
this.cleanupMondayFirstColumnAfterTest = false;
if (shouldClearMondayFirstColumn) {
return this.clearMondayFirstColumn();
}
};
disableStundenraster = () => {
this.getStundenrasterToggle().then(($toggle) => {
if ($toggle.find(".fa-toggle-on").length) {
@@ -395,7 +359,9 @@ class TempusPage {
}
});
this.getStundenrasterToggle().find("i").should("have.class", "fa-toggle-off");
this.getStundenrasterToggle()
.find("i")
.should("have.class", "fa-toggle-off");
};
waitForCalendarToFinishLoading = () => {
@@ -404,10 +370,35 @@ class TempusPage {
setCurrentSemester = () => {
this.getSemesterSetterButton().click();
//waitForOk("@getCurrentSemester");
//waitForOk("@setSemester");
}
};
dropEventOnCalendarPart = (eventId, partIndex, options = {}) => {
const event = this.getCalendarEventById(eventId);
return event
.should("be.visible")
.drag(this.getCalendarPartDropTarget(partIndex), {
waitForAnimations: false,
animationDistanceThreshold: 0,
...options,
});
};
dropCourseOnCalendarPart = (courseIndex, partIndex, options = {}) => {
const course = this.getCoursePickerRows()
.eq(courseIndex)
.find("span")
.first();
return course
.should("be.visible")
.drag(this.getCalendarPartDropTarget(partIndex), {
waitForAnimations: false,
animationDistanceThreshold: 0,
scrollBehavior: "top",
...options,
});
};
}
export const tempusPage = new TempusPage();