From 40c18d3603113f773784605522b4157f39d49412 Mon Sep 17 00:00:00 2001 From: Ivymaster Date: Sun, 31 May 2026 23:03:16 +0200 Subject: [PATCH] Add basic tests for tempus feature --- .../Calendar/Base/Grid/Line/Event.js | 1 + tests/cypress/cypress.config.js | 4 +- .../cypress/e2e/specs/ui/tempus/tempus.cy.js | 1046 +++++++++++++---- .../cypress/support/pages/tempus.po.js | 112 +- 4 files changed, 927 insertions(+), 236 deletions(-) diff --git a/public/js/components/Calendar/Base/Grid/Line/Event.js b/public/js/components/Calendar/Base/Grid/Line/Event.js index a3017aba0..e75e07503 100644 --- a/public/js/components/Calendar/Base/Grid/Line/Event.js +++ b/public/js/components/Calendar/Base/Grid/Line/Event.js @@ -110,6 +110,7 @@ export default { :class="classes" style="z-index: 2" :draggable="draggable" + :data-id="'event-' + event.orig.kalender_id" ref="eventEl" @dragstart="onDragStart" v-draggable:move.noimage="draggable ? dragKalenderCollection : {}" diff --git a/tests/cypress/cypress.config.js b/tests/cypress/cypress.config.js index bb7cd2d09..8b3b91217 100644 --- a/tests/cypress/cypress.config.js +++ b/tests/cypress/cypress.config.js @@ -9,8 +9,8 @@ module.exports = defineConfig({ defaultCommandTimeout: 20000, pageLoadTimeout: 10000, retries: { - runMode: 2, // when running via CLI / CI - openMode: 2 // when running in interactive mode + runMode: 1, + openMode: 1 } }, env: { diff --git a/tests/cypress/cypress/e2e/specs/ui/tempus/tempus.cy.js b/tests/cypress/cypress/e2e/specs/ui/tempus/tempus.cy.js index 76fa352aa..0ad19e367 100644 --- a/tests/cypress/cypress/e2e/specs/ui/tempus/tempus.cy.js +++ b/tests/cypress/cypress/e2e/specs/ui/tempus/tempus.cy.js @@ -15,7 +15,7 @@ const waitForOk = (alias) => cy.wait(alias).its("response.statusCode").should("eq", 200); const syncAndReloadPlanner = () => { - tempusPage.selectPreviewRole(SYNC); // ensure all events are loaded + tempusPage.selectPreviewRole(SYNC); waitForOk("@syncCalendar"); waitForOk("@fetchPlanData"); tempusPage.waitForCalendarToFinishLoading(); @@ -27,10 +27,110 @@ const selectRoleAndWait = (role) => { tempusPage.waitForCalendarToFinishLoading(); }; -const formatIsoDateTimeForKalenderApi = (isoDateTime) => - isoDateTime.slice(0, 16).replace("T", " "); +const getCalendarEventData = ($event) => + JSON.parse($event.attr("data-fhc-draggable-value")); -let eventRestoreOnFailure = null; +const getFirstLecturer = (eventData) => + eventData?.orig?.lektor?.find((lecturer) => lecturer?.kurzbz); + +const getUpdatedKalenderId = (interception) => { + const retval = interception.response.body?.data?.retval; + + return retval?.kalender_id ?? retval; +}; + +const expectEventRoom = (eventId, expectedRoom) => { + tempusPage + .getCalendarEventRoom(eventId) + .scrollIntoView() + .should("be.visible") + .invoke("text") + .then((roomText) => { + expect(roomText.trim(), "event room").to.eq(expectedRoom); + }); +}; + +let cleanupMondayFirstColumnAfterTest = false; + +const getKalenderId = (eventData) => + eventData?.orig?.kalender_id ?? eventData?.id; + +const getMondayFirstColumnEventData = ($body) => + [ + ...$body + .find( + `${tempusPage.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); + +const reloadPlannerForCleanup = () => { + tempusPage.visit(); + waitForOk("@fetchCourseTree"); + waitForOk("@fetchPlanData"); + + return tempusPage.waitForCalendarToFinishLoading(); +}; + +const deleteKalenderEvent = (kalenderId) => + cy.request({ + method: "POST", + url: "/index.ci.php/api/frontend/v1/tempus/Kalender/deleteEntry", + form: true, + body: { + kalender_id: kalenderId, + }, + failOnStatusCode: false, + }); + +const clearMondayFirstColumn = () => { + reloadPlannerForCleanup(); + + return cy.get("body").then(($body) => { + const calendarIds = [ + ...new Set( + getMondayFirstColumnEventData($body).map(getKalenderId).filter(Boolean), + ), + ]; + + if (!calendarIds.length) { + return; + } + + return cy + .wrap(calendarIds, { log: false }) + .each((kalenderId) => { + return deleteKalenderEvent(kalenderId).then((response) => { + if (response.status !== 200) { + Cypress.log({ + name: "tempus cleanup", + message: `Could not delete calendar event ${kalenderId}: ${response.status}`, + }); + } + }); + }) + .then(() => { + return reloadPlannerForCleanup(); + }); + }); +}; + +const clearMondayFirstColumnBeforeAndAfter = () => { + cleanupMondayFirstColumnAfterTest = true; + + return clearMondayFirstColumn(); +}; context("Base tempus tests", () => { beforeEach(() => { @@ -52,10 +152,18 @@ context("Base tempus tests", () => { cy.intercept({ method: "GET", url: "**/tempus/Kalender/getHistory**" }).as( "fetchEventHistory", ); + cy.intercept({ + method: "GET", + url: "**/tempus/Kalender/getRaumvorschlag**", + }).as("fetchRoomSuggestions"); cy.intercept({ method: "GET", url: "**/tempus/coursepicker/getByStg**", }).as("fetchCoursePickerCourses"); + cy.intercept({ + method: "POST", + url: "**/searchbar/search**", + }).as("searchbarSearch"); cy.intercept({ method: "POST", url: "**/tempus/Kalender/addKalenderEvent**", @@ -68,10 +176,6 @@ context("Base tempus tests", () => { method: "POST", url: "**/tempus/Kalender/sync**", }).as("syncCalendar"); - cy.intercept({ - method: "POST", - url: "**/tempus/Kalender/syncToLecturer**", - }).as("syncCalendarToLecturer"); cy.intercept({ method: "POST", url: "**/tempus/Kalender/syncToStudent**", @@ -85,206 +189,695 @@ context("Base tempus tests", () => { waitForOk("@fetchCourseTree"); waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); }); - afterEach(function () { - const eventRestore = eventRestoreOnFailure; - eventRestoreOnFailure = null; + afterEach(() => { + const shouldCleanupMondayFirstColumn = cleanupMondayFirstColumnAfterTest; + cleanupMondayFirstColumnAfterTest = false; - if (this.currentTest.state !== "failed" || !eventRestore) { - return; + if (shouldCleanupMondayFirstColumn) { + return clearMondayFirstColumn(); } - - return tempusPage - .restoreKalenderEventTime( - eventRestore.kalenderId, - eventRestore.startTime, - eventRestore.endTime, - { failOnStatusCode: false }, - ) - .its("status") - .should("eq", 200); }); - // it("can access Tempus page with valid credentials", () => { - // tempusPage.getTempusOverview().should("be.visible"); - // }); + it("can access Tempus page with valid credentials", () => { + tempusPage.getTempusOverview().should("be.visible"); + }); - // it("shows all expected page elements", () => { - // tempusPage.getTempusOverview().should("be.visible"); - // tempusPage.getSidebarMenu().should("exist"); - // tempusPage.getSlideInCoursesMenu().should("exist"); - // tempusPage.getCalendarSection().should("be.visible"); - // tempusPage.getPreviewRoleOptionsHolder().should("be.visible"); - // }); + it("shows all expected page elements", () => { + tempusPage.getTempusOverview().should("be.visible"); + tempusPage.getSidebarMenu().should("exist"); + tempusPage.getSlideInCoursesMenu().should("exist"); + tempusPage.getCalendarSection().should("be.visible"); + tempusPage.getPreviewRoleOptionsHolder().should("be.visible"); + }); - // it("can open courses menu", () => { - // tempusPage.getSlideInCoursesMenu().should("exist").should("be.hidden"); + it("can open courses menu", () => { + tempusPage.getSlideInCoursesMenu().should("exist").should("be.hidden"); - // tempusPage.openCoursesMenu(); + tempusPage.openCoursesMenu(); - // tempusPage.getCourseTreeRows().should("have.length.greaterThan", 0); - // }); + tempusPage.getCourseTreeRows().should("have.length.greaterThan", 0); + }); - // it("can select one course and show preview of its events", () => { - // tempusPage.getSlideInCoursesMenu().should("exist"); - // tempusPage.getCourseTreeRows().should("have.length.greaterThan", 0); - // tempusPage.getCoursePicker().should("exist"); - // tempusPage.getCoursePickerRows().should("have.length", 0); + it("can select one course and show preview of its events", () => { + tempusPage.getSlideInCoursesMenu().should("exist"); + tempusPage.getCourseTreeRows().should("have.length.greaterThan", 0); + tempusPage.getCoursePicker().should("exist"); + tempusPage.getCoursePickerRows().should("have.length", 0); - // tempusPage.selectFirstCourse(); - // waitForOk("@fetchCoursePickerCourses"); + tempusPage.selectFirstCourse(); + waitForOk("@fetchCoursePickerCourses"); - // tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); - // }); + tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); + }); - // it("can search for a course event in the course picker", () => { - // tempusPage.selectFirstCourse(); - // waitForOk("@fetchCoursePickerCourses"); + it("can search for a course event in the course picker", () => { + tempusPage.selectFirstCourse(); + waitForOk("@fetchCoursePickerCourses"); - // tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); + tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); - // tempusPage - // .getCoursePickerRows() - // .last() - // .find("div:first span:first") - // .invoke("text") - // .as("randomCourseText"); + tempusPage + .getCoursePickerRows() + .last() + .find("div:first span:first") + .invoke("text") + .as("randomCourseText"); - // cy.get("@randomCourseText").then((randomCourseText) => { - // tempusPage.getCoursePickerSearchInput().type(randomCourseText); + cy.get("@randomCourseText").then((randomCourseText) => { + tempusPage.getCoursePickerSearchInput().type(randomCourseText); - // tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); - // tempusPage - // .getCoursePickerRows() - // .first() - // .should("contain.text", randomCourseText); - // }); - // }); + tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); + tempusPage + .getCoursePickerRows() + .first() + .should("contain.text", randomCourseText); + }); + }); - // it("can drag and drop one course event into the calendar", () => { - // tempusPage.getCalendarSection().should("exist"); - // tempusPage.waitForCalendarToFinishLoading(); + it("can drag and drop one course event into the calendar", () => { + clearMondayFirstColumnBeforeAndAfter(); - // tempusPage.selectFirstCourse(); - // waitForOk("@fetchCoursePickerCourses"); + tempusPage.getCalendarSection().should("exist"); + tempusPage.waitForCalendarToFinishLoading(); - // tempusPage.getCalendarEvents().then(($events) => { - // cy.wrap($events.length).as("initialEventCount"); - // }); + tempusPage.selectFirstCourse(); + waitForOk("@fetchCoursePickerCourses"); - // tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); - // tempusPage.dropCourseOnCalendarPart(0, 10); + tempusPage.getCalendarEvents().then(($events) => { + cy.wrap($events.length).as("initialEventCount"); + }); - // waitForOk("@addCalendarEvent"); - // waitForOk("@fetchPlanData"); + tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); + tempusPage.dropCourseOnCalendarPart(0, 10); - // tempusPage.waitForCalendarToFinishLoading(); + waitForOk("@addCalendarEvent"); + waitForOk("@fetchPlanData"); - // cy.get("@initialEventCount").then((initialEventCount) => { - // tempusPage - // .getCalendarEvents() - // .should("have.length", initialEventCount + 1); - // }); - // }); + tempusPage.waitForCalendarToFinishLoading(); - // it("shows the same number of calendar events for all role previews", () => { - // const rolePreviews = [ - // { label: PLANER, fetchAlias: roleFetchAliases[PLANER] }, - // { label: LEKTOR, fetchAlias: roleFetchAliases[LEKTOR] }, - // { label: STUDENT, fetchAlias: roleFetchAliases[STUDENT] }, - // ]; - // const eventCounts = {}; + cy.get("@initialEventCount").then((initialEventCount) => { + tempusPage + .getCalendarEvents() + .should("have.length", initialEventCount + 1); + }); + }); - // syncAndReloadPlanner(); + it("shows the same number of calendar events for all role previews", () => { + const rolePreviews = [ + { label: PLANER, fetchAlias: roleFetchAliases[PLANER] }, + { label: LEKTOR, fetchAlias: roleFetchAliases[LEKTOR] }, + { label: STUDENT, fetchAlias: roleFetchAliases[STUDENT] }, + ]; + const eventCounts = {}; - // cy.wrap(rolePreviews) - // .each((rolePreview) => { - // selectRoleAndWait(rolePreview.label); + syncAndReloadPlanner(); - // tempusPage.getCalendarEvents().then(($events) => { - // eventCounts[rolePreview.label] = $events.length; - // }); - // }) - // .then(() => { - // const expectedCount = eventCounts[rolePreviews[0].label]; + cy.wrap(rolePreviews) + .each((rolePreview) => { + selectRoleAndWait(rolePreview.label); - // Object.entries(eventCounts).forEach(([role, count]) => { - // expect(count, `${role} calendar event count`).to.eq(expectedCount); - // }); - // }); - // }); + tempusPage.getCalendarEvents().then(($events) => { + eventCounts[rolePreview.label] = $events.length; + }); + }) + .then(() => { + const expectedCount = eventCounts[rolePreviews[0].label]; - // it("shows event details modal when clicking a calendar event", () => { - // tempusPage.waitForCalendarToFinishLoading(); - // tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + Object.entries(eventCounts).forEach(([role, count]) => { + expect(count, `${role} calendar event count`).to.eq(expectedCount); + }); + }); + }); - // tempusPage.getCalendarEvents().first().click(); + it("shows event details modal when clicking a calendar event", () => { + tempusPage.waitForCalendarToFinishLoading(); + tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); - // tempusPage.getCalendarEventModal().should("be.visible"); - // }); + tempusPage.getCalendarEvents().first().click(); - // it("shows event context menu when right clicking a calendar event", () => { - // tempusPage.waitForCalendarToFinishLoading(); - // tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + tempusPage.getCalendarEventModal().should("be.visible"); + }); - // tempusPage.getCalendarEvents().first().rightclick(); + it("shows event context menu when right clicking a calendar event", () => { + tempusPage.waitForCalendarToFinishLoading(); + tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); - // tempusPage.getEventContextMenu().should("be.visible"); - // }); + tempusPage.getCalendarEvents().first().rightclick(); - // it("shows history modal when selecting History from event context menu", () => { - // tempusPage.waitForCalendarToFinishLoading(); - // tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + tempusPage.getEventContextMenu().should("be.visible"); + }); - // tempusPage.getCalendarEvents().first().rightclick(); - // tempusPage.getEventContextMenuOption("History").click(); - // waitForOk("@fetchEventHistory"); + it("shows Raumauswahl modal when selecting Raumauswahl from event context menu", () => { + tempusPage.waitForCalendarToFinishLoading(); + tempusPage + .getCalendarEventsWithLehreinheit() + .should("have.length.greaterThan", 0); - // tempusPage.getHistoryModal().should("be.visible"); - // }); + tempusPage.getCalendarEventsWithLehreinheit().first().rightclick(); + tempusPage.getEventContextMenuOption("Raumauswahl").click(); + waitForOk("@fetchRoomSuggestions"); - // it.skip("shows reservation modal when dropping reservation handle on calendar", () => { - // tempusPage.waitForCalendarToFinishLoading(); - // tempusPage.getCalendarBaseGrid().should("be.visible"); - // cy.wait(1000); // wait for potential animations - // tempusPage - // .getReservationDragHandle() - // .drag( - // tempusPage.selectors.calendarBaseGrid + " .part-body:nth-of-type(18)", - // { - // waitForAnimations: false, - // animationDistanceThreshold: 0, - // }, - // ); + tempusPage.getRaumauswahlModal().should("be.visible"); + }); - // cy.wait(3000); // wait for potential animations - // tempusPage.getReservationModal().should("be.visible"); - // }); + it("room change on planner preview updates planner event, but keeps original room on other previews", () => { + clearMondayFirstColumnBeforeAndAfter(); - // it("can drop event from calendar into parking slot", () => { - // tempusPage.getEventParkingSlot().should("exist"); - // tempusPage.getParkedEvents().should("have.length", 0); + syncAndReloadPlanner(); - // tempusPage - // .getCalendarEvents() - // .first() - // .drag(tempusPage.selectors.parkingSlot); + tempusPage + .getCalendarEventsWithLehreinheitAndRoom() + .should("have.length.greaterThan", 0); - // tempusPage.getParkedEvents().should("have.length", 1); - // }); + tempusPage + .getCalendarEventsWithLehreinheitAndRoom() + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const eventData = JSON.parse(eventJSON); + 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; + + expectEventRoom(eventId, originalRoom); + + tempusPage + .getCalendarEventById(eventId) + .should("be.visible") + .rightclick(); + tempusPage.getEventContextMenuOption("Raumauswahl").click(); + waitForOk("@fetchRoomSuggestions"); + + tempusPage + .getRaumauswahlRoomOptions() + .should("have.length.greaterThan", 0) + .then(($roomOptions) => { + const roomOption = [...$roomOptions].find((option) => { + const room = option.innerText.trim(); + + return room && room !== originalRoom; + }); + + expect(roomOption, "different room suggestion").to.exist; + + cy.wrap(roomOption.innerText.trim()).as("newRoom"); + cy.wrap(roomOption).click(); + }); + + cy.wait("@updateCalendarEvent").then((interception) => { + expect(interception.response.statusCode).to.eq(200); + + const updatedEventId = getUpdatedKalenderId(interception); + expect(updatedEventId, "updated planner event id").to.exist; + + cy.wrap(updatedEventId).as("updatedEventId"); + }); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + cy.get("@newRoom").then((newRoom) => { + cy.get("@updatedEventId").then((updatedEventId) => { + expectEventRoom(updatedEventId, newRoom); + + selectRoleAndWait(LEKTOR); + expectEventRoom(eventId, originalRoom); + + selectRoleAndWait(STUDENT); + expectEventRoom(eventId, originalRoom); + }); + }); + }); + }); + + it("sync after planner preview room change loads new room on all previews", () => { + clearMondayFirstColumnBeforeAndAfter(); + + syncAndReloadPlanner(); + + tempusPage + .getCalendarEventsWithLehreinheitAndRoom() + .should("have.length.greaterThan", 0); + + tempusPage + .getCalendarEventsWithLehreinheitAndRoom() + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const eventData = JSON.parse(eventJSON); + 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; + + tempusPage + .getCalendarEventById(eventId) + .scrollIntoView() + .should("be.visible") + .rightclick(); + tempusPage.getEventContextMenuOption("Raumauswahl").click(); + waitForOk("@fetchRoomSuggestions"); + + tempusPage + .getRaumauswahlRoomOptions() + .should("have.length.greaterThan", 0) + .then(($roomOptions) => { + const roomOption = [...$roomOptions].find((option) => { + const room = option.innerText.trim(); + + return room && room !== originalRoom; + }); + + expect(roomOption, "different room suggestion").to.exist; + + cy.wrap(roomOption.innerText.trim()).as("newRoom"); + cy.wrap(roomOption).click(); + }); + + cy.wait("@updateCalendarEvent").then((interception) => { + expect(interception.response.statusCode).to.eq(200); + + const updatedEventId = getUpdatedKalenderId(interception); + expect(updatedEventId, "updated planner event id").to.exist; + + cy.wrap(updatedEventId).as("updatedEventId"); + }); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + cy.get("@newRoom").then((newRoom) => { + cy.get("@updatedEventId").then((updatedEventId) => { + expectEventRoom(updatedEventId, newRoom); + + syncAndReloadPlanner(); + expectEventRoom(updatedEventId, newRoom); + + selectRoleAndWait(LEKTOR); + expectEventRoom(updatedEventId, newRoom); + + selectRoleAndWait(STUDENT); + expectEventRoom(updatedEventId, newRoom); + }); + }); + }); + }); + + it("shows history modal when selecting History from event context menu", () => { + tempusPage.waitForCalendarToFinishLoading(); + tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + + tempusPage.getCalendarEvents().first().rightclick(); + tempusPage.getEventContextMenuOption("History").click(); + waitForOk("@fetchEventHistory"); + + tempusPage.getHistoryModal().should("be.visible"); + }); + + it.skip("shows reservation modal when dropping reservation handle on calendar", () => { + tempusPage.waitForCalendarToFinishLoading(); + tempusPage.getCalendarBaseGrid().should("be.visible"); + cy.wait(1000); + tempusPage + .getReservationDragHandle() + .drag( + tempusPage.selectors.calendarBaseGrid + " .part-body:nth-of-type(18)", + { + waitForAnimations: false, + animationDistanceThreshold: 0, + }, + ); + + cy.wait(3000); + tempusPage.getReservationModal().should("be.visible"); + }); + + it("can drop event from calendar into parking slot", () => { + tempusPage.getEventParkingSlot().should("exist"); + tempusPage.getParkedEvents().should("have.length", 0); + + tempusPage + .getCalendarEvents() + .first() + .drag(tempusPage.selectors.parkingSlot); + + tempusPage.getParkedEvents().should("have.length", 1); + }); + + it("filters calendar events by selecting the first event room in the navbar", () => { + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getCalendarEventsWithRoom() + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const selectedRoom = JSON.parse(eventJSON)?.orig?.ort_kurzbz; + expect(selectedRoom, "first event room").to.be.a("string").and.not.be + .empty; + expect( + selectedRoom.length, + "room search query length", + ).to.be.greaterThan(1); + + tempusPage.getNavbarSearchInput().clear().type(selectedRoom, { + parseSpecialCharSequences: false, + }); + waitForOk("@searchbarSearch"); + + tempusPage + .getNavbarRoomSearchResult(selectedRoom) + .should("be.visible") + .click(); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage.getSelectedRoomIndicator(selectedRoom).should("be.visible"); + tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + tempusPage.getCalendarEvents().each(($event) => { + const eventData = getCalendarEventData($event); + + expect(eventData?.orig?.ort_kurzbz, "event data room").to.eq( + selectedRoom, + ); + cy.wrap($event) + .find(".event-place") + .invoke("text") + .then((eventPlace) => { + expect(eventPlace.trim(), "event body room").to.eq(selectedRoom); + }); + }); + }); + }); + + it("removes the selected room filter", () => { + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getCalendarEventsWithRoom() + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const selectedRoom = JSON.parse(eventJSON)?.orig?.ort_kurzbz; + expect(selectedRoom, "first event room").to.be.a("string").and.not.be + .empty; + expect( + selectedRoom.length, + "room search query length", + ).to.be.greaterThan(1); + + tempusPage.getCalendarEvents().then(($events) => { + const originalEventCount = $events.length; + cy.wrap(originalEventCount).as("originalEventCount"); + }); + + tempusPage.getNavbarSearchInput().clear().type(selectedRoom, { + parseSpecialCharSequences: false, + }); + waitForOk("@searchbarSearch"); + + tempusPage + .getNavbarRoomSearchResult(selectedRoom) + .should("be.visible") + .click(); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getSelectedRoomIndicator(selectedRoom) + .should("be.visible"); + tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + + tempusPage + .getSelectedRoomRemoveButton(selectedRoom) + .click(); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage.getSelectedRoomIndicator(selectedRoom).should("not.exist"); + cy.get("@originalEventCount").then((originalEventCount) => { + tempusPage + .getCalendarEvents() + .should("have.length", originalEventCount); + }); + }); + }); + + it("filters calendar events by selecting the first event lecturer in the navbar", () => { + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getCalendarEventsWithLecturer() + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const selectedLecturer = getFirstLecturer(JSON.parse(eventJSON)); + expect(selectedLecturer?.kurzbz, "first event lecturer").to.exist; + + const lecturerSearch = + selectedLecturer.mitarbeiter_uid ?? + selectedLecturer.uid ?? + selectedLecturer.kurzbz; + expect(lecturerSearch, "lecturer search query").to.be.a("string").and + .not.be.empty; + + tempusPage.getNavbarSearchInput().clear().type(lecturerSearch, { + parseSpecialCharSequences: false, + }); + waitForOk("@searchbarSearch"); + + tempusPage + .getFirstNavbarEmployeeSearchResult() + .should("be.visible") + .invoke("text") + .then((lecturerName) => { + const selectedLecturerName = lecturerName.trim(); + + tempusPage.getFirstNavbarEmployeeSearchResult().click(); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getSelectedLecturerIndicator(selectedLecturerName) + .should("be.visible"); + tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + tempusPage.getCalendarEvents().each(($event) => { + const eventData = getCalendarEventData($event); + const eventLecturers = eventData?.orig?.lektor ?? []; + + expect( + eventLecturers.map((lecturer) => lecturer.kurzbz), + "event data lecturers", + ).to.include(selectedLecturer.kurzbz); + cy.wrap($event) + .find(".event-lectors") + .then(($lecturers) => { + const bodyLecturers = [...$lecturers].map((lecturer) => + lecturer.innerText.trim(), + ); + + expect(bodyLecturers, "event body lecturers").to.include( + selectedLecturer.kurzbz, + ); + }); + }); + }); + }); + }); + + it("removes the selected lecturer filter", () => { + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getCalendarEventsWithLecturer() + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const selectedLecturer = getFirstLecturer(JSON.parse(eventJSON)); + expect(selectedLecturer?.kurzbz, "first event lecturer").to.exist; + + const lecturerSearch = + selectedLecturer.mitarbeiter_uid ?? + selectedLecturer.uid ?? + selectedLecturer.kurzbz; + expect(lecturerSearch, "lecturer search query").to.be.a("string").and + .not.be.empty; + + tempusPage.getCalendarEvents().then(($events) => { + const originalEventCount = $events.length; + cy.wrap(originalEventCount).as("originalEventCount"); + }); + + tempusPage.getNavbarSearchInput().clear().type(lecturerSearch, { + parseSpecialCharSequences: false, + }); + waitForOk("@searchbarSearch"); + + tempusPage + .getFirstNavbarEmployeeSearchResult() + .should("be.visible") + .invoke("text") + .then((lecturerName) => { + const selectedLecturerName = lecturerName.trim(); + + tempusPage.getFirstNavbarEmployeeSearchResult().click(); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getSelectedLecturerIndicator(selectedLecturerName) + .should("be.visible"); + tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + + tempusPage + .getSelectedLecturerRemoveButton(selectedLecturerName) + .click(); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getSelectedLecturerIndicator(selectedLecturerName) + .should("not.exist"); + cy.get("@originalEventCount").then((originalEventCount) => { + tempusPage + .getCalendarEvents() + .should("have.length", originalEventCount); + }); + }); + }); + }); + + it("hides calendar events when the selected lecturer plan toggle is deselected", () => { + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getCalendarEventsWithLecturer() + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const selectedLecturer = getFirstLecturer(JSON.parse(eventJSON)); + expect(selectedLecturer?.kurzbz, "first event lecturer").to.exist; + + const lecturerSearch = + selectedLecturer.mitarbeiter_uid ?? + selectedLecturer.uid ?? + selectedLecturer.kurzbz; + expect(lecturerSearch, "lecturer search query").to.be.a("string").and + .not.be.empty; + + tempusPage.getNavbarSearchInput().clear().type(lecturerSearch, { + parseSpecialCharSequences: false, + }); + waitForOk("@searchbarSearch"); + + tempusPage + .getFirstNavbarEmployeeSearchResult() + .should("be.visible") + .invoke("text") + .then((lecturerName) => { + const selectedLecturerName = lecturerName.trim(); + + tempusPage.getFirstNavbarEmployeeSearchResult().click(); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getSelectedLecturerIndicator(selectedLecturerName) + .should("be.visible"); + tempusPage.getCalendarEvents().should("have.length.greaterThan", 0); + + tempusPage + .getSelectedLecturerPlanToggle(selectedLecturerName) + .click(); + tempusPage.getCalendarEvents().should("not.exist"); + }); + }); + }); + + it("shows lecturer wish overlays when the selected lecturer wish toggle is disabled", () => { + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getCalendarEventsWithLecturer() + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const selectedLecturer = getFirstLecturer(JSON.parse(eventJSON)); + expect(selectedLecturer?.kurzbz, "first event lecturer").to.exist; + + const lecturerSearch = + selectedLecturer.mitarbeiter_uid ?? + selectedLecturer.uid ?? + selectedLecturer.kurzbz; + expect(lecturerSearch, "lecturer search query").to.be.a("string").and + .not.be.empty; + + tempusPage.getNavbarSearchInput().clear().type(lecturerSearch, { + parseSpecialCharSequences: false, + }); + waitForOk("@searchbarSearch"); + + tempusPage + .getFirstNavbarEmployeeSearchResult() + .should("be.visible") + .invoke("text") + .then((lecturerName) => { + const selectedLecturerName = lecturerName.trim(); + + tempusPage.getFirstNavbarEmployeeSearchResult().click(); + waitForOk("@fetchPlanData"); + + tempusPage.waitForCalendarToFinishLoading(); + + tempusPage + .getSelectedLecturerIndicator(selectedLecturerName) + .should("be.visible"); + tempusPage + .getSelectedLecturerWishToggle(selectedLecturerName) + .should("be.visible") + .click(); + tempusPage + .getLecturerWishOverlays() + .should("have.length", 0) + + tempusPage + .getSelectedLecturerWishToggle(selectedLecturerName) + .should("be.visible") + .click(); + tempusPage.getLecturerWishOverlays().should("have.length.greaterThan", 0); + }); + }); + }); it("on planner event change shows unchanged event on other roles", () => { + clearMondayFirstColumnBeforeAndAfter(); + syncAndReloadPlanner(); cy.get(".fhc-calendar-base-grid > div") - .last() - .invoke( - "css", - "overflow", - "hidden", - ); - cy.wait(500); // ensure overflow change is applied before dragging, otherwise the drag can fail due to the calendar scrolling + .last() + .invoke("css", "overflow", "hidden"); + cy.wait(500); tempusPage .getCalendarEventsByTimeRange("08:00:00", "08:45:00") .first() @@ -292,17 +885,12 @@ context("Base tempus tests", () => { .then((eventJSON) => { expect(eventJSON).to.exist; - const eventData = JSON.parse(eventJSON); - const originalStartTime = formatIsoDateTimeForKalenderApi( - eventData.orig.isostart, - ); - const originalEndTime = formatIsoDateTimeForKalenderApi( - eventData.orig.isoend, - ); - let eventId = eventData?.id; + let eventId = JSON.parse(eventJSON)?.id; expect(eventId).to.exist; - tempusPage.dropEventOnCalendarPart(eventId, 2); + tempusPage.dropEventOnCalendarPart(eventId, 3, { + scrollIntoView: true, + }); cy.wait("@updateCalendarEvent").then((interception) => { expect(interception.response.statusCode).to.eq(200); @@ -310,12 +898,6 @@ context("Base tempus tests", () => { const updatedEventId = interception.response.body.data.retval.kalender_id; - eventRestoreOnFailure = { - kalenderId: updatedEventId, - startTime: originalStartTime, - endTime: originalEndTime, - }; - cy.wrap(updatedEventId).as("updatedEventId"); }); waitForOk("@fetchPlanData"); @@ -343,6 +925,8 @@ context("Base tempus tests", () => { }); it("sync after planner preview event change loads event in same way on all previews", () => { + clearMondayFirstColumnBeforeAndAfter(); + syncAndReloadPlanner(); tempusPage @@ -352,17 +936,10 @@ context("Base tempus tests", () => { .then((eventJSON) => { expect(eventJSON).to.exist; - const eventData = JSON.parse(eventJSON); - const originalStartTime = formatIsoDateTimeForKalenderApi( - eventData.orig.isostart, - ); - const originalEndTime = formatIsoDateTimeForKalenderApi( - eventData.orig.isoend, - ); - let eventId = eventData?.id; + let eventId = JSON.parse(eventJSON)?.id; expect(eventId).to.exist; - tempusPage.dropEventOnCalendarPart(eventId, 3, { + tempusPage.dropEventOnCalendarPart(eventId, 4, { scrollIntoView: true, }); @@ -372,12 +949,6 @@ context("Base tempus tests", () => { const updatedEventId = interception.response.body.data.retval.kalender_id; - eventRestoreOnFailure = { - kalenderId: updatedEventId, - startTime: originalStartTime, - endTime: originalEndTime, - }; - cy.wrap(updatedEventId).as("updatedEventId"); }); waitForOk("@fetchPlanData"); @@ -405,6 +976,8 @@ context("Base tempus tests", () => { }); it("live unlock after planner preview event change loads event in same way on planner and lektor, but not on student preview", () => { + clearMondayFirstColumnBeforeAndAfter(); + syncAndReloadPlanner(); tempusPage @@ -416,7 +989,7 @@ context("Base tempus tests", () => { let eventId = JSON.parse(eventJSON)?.id; expect(eventId).to.exist; - tempusPage.dropEventOnCalendarPart(eventId, 4, { + tempusPage.dropEventOnCalendarPart(eventId, 5, { scrollIntoView: true, }); @@ -432,7 +1005,7 @@ context("Base tempus tests", () => { .getCalendarEventById(updatedEventId) .should("exist") .rightclick(); - tempusPage.getEventContextMenuOption("Freischalten für Live").click(); // TODO: Check wether live is for studnet + tempusPage.getEventContextMenuOption("Freischalten für Live").click(); waitForOk("@syncCalendarToStudent"); tempusPage.getEventGridRow(updatedEventId).then((gridRowStyle) => { @@ -519,6 +1092,8 @@ context("Base tempus tests", () => { }); it("event deletion on planner preview preservers event on planner, but shows it as unsynced on lektor and student preview", () => { + clearMondayFirstColumnBeforeAndAfter(); + syncAndReloadPlanner(); tempusPage @@ -552,6 +1127,8 @@ context("Base tempus tests", () => { }); it("syncing event deletion on planner preview removes event from other previews", () => { + clearMondayFirstColumnBeforeAndAfter(); + syncAndReloadPlanner(); tempusPage @@ -585,6 +1162,8 @@ context("Base tempus tests", () => { }); it("can bottom resize an event on planner preview", () => { + clearMondayFirstColumnBeforeAndAfter(); + tempusPage.getCalendarSection().should("exist"); tempusPage.waitForCalendarToFinishLoading(); @@ -642,6 +1221,8 @@ context("Base tempus tests", () => { }); it("can top resize an event on planner preview", () => { + clearMondayFirstColumnBeforeAndAfter(); + tempusPage.getCalendarSection().should("exist"); tempusPage.waitForCalendarToFinishLoading(); @@ -698,13 +1279,78 @@ context("Base tempus tests", () => { }); }); - // check live issue -- done - // check delete -- DONE - // resize - dones - // sve al bez stunden raster - // course picker search - // raumauswal - // btn click to current semester - // filter za raum und fuer person - // filter ode u sidebar + it("can drag and drop one course event into the calendar when Stundenraster is disabled", () => { + clearMondayFirstColumnBeforeAndAfter(); + + tempusPage.getCalendarSection().should("exist"); + tempusPage.waitForCalendarToFinishLoading(); + tempusPage.disableStundenraster(); + + tempusPage.selectFirstCourse(); + waitForOk("@fetchCoursePickerCourses"); + + tempusPage.getCalendarEvents().then(($events) => { + cy.wrap($events.length).as("initialEventCount"); + }); + + tempusPage.getCoursePickerRows().should("have.length.greaterThan", 0); + tempusPage.dropCourseOnCalendarPart(0, 1); + + waitForOk("@addCalendarEvent"); + waitForOk("@fetchPlanData"); + + tempusPage.waitForCalendarToFinishLoading(); + + cy.get("@initialEventCount").then((initialEventCount) => { + tempusPage + .getCalendarEvents() + .should("have.length", initialEventCount + 1); + }); + }); + + + + it("can drag and drop an event on part 6 when Stundenraster is disabled", () => { + clearMondayFirstColumnBeforeAndAfter(); + + syncAndReloadPlanner(); + tempusPage.disableStundenraster(); + + tempusPage + .getCalendarEventsByTimeRange("08:00:00", "08:45:00") + .first() + .invoke("attr", "data-fhc-draggable-value") + .then((eventJSON) => { + expect(eventJSON).to.exist; + + const eventId = JSON.parse(eventJSON)?.id; + expect(eventId).to.exist; + + tempusPage.getEventGridRow(eventId).as("originalGridRow"); + tempusPage.dropEventOnCalendarPart(eventId, 6, { + scrollIntoView: true, + }); + + cy.wait("@updateCalendarEvent").then((interception) => { + expect(interception.response.statusCode).to.eq(200); + + const updatedEventId = getUpdatedKalenderId(interception); + expect(updatedEventId, "updated planner event id").to.exist; + + cy.wrap(updatedEventId).as("updatedEventId"); + }); + waitForOk("@fetchPlanData"); + tempusPage.waitForCalendarToFinishLoading(); + + 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); + }); + }); + }); + }); + }); }); diff --git a/tests/cypress/cypress/support/pages/tempus.po.js b/tests/cypress/cypress/support/pages/tempus.po.js index 4e3f386f1..68cfb7319 100644 --- a/tests/cypress/cypress/support/pages/tempus.po.js +++ b/tests/cypress/cypress/support/pages/tempus.po.js @@ -1,6 +1,5 @@ class TempusPage { selectors = { - calendarSection: "div[data-cy='tempus'] .fhc-calendar-base", calendarBaseGrid: "div[data-cy='tempus'] .fhc-calendar-base-grid", parkingSlot: "div[data-cy='tempus'] #parkingslot", }; @@ -8,6 +7,8 @@ class TempusPage { visit = () => cy.visit("/index.ci.php/tempus", { onBeforeLoad(win) { win.localStorage.removeItem("tempus_parking"); + win.localStorage.removeItem("tempus_searchtypes"); + win.sessionStorage.removeItem("tempus_searchstr"); }, }); @@ -22,59 +23,87 @@ class TempusPage { getCourseTreeRows = () => this.getSlideInCoursesMenu().find(".p-treetable-tbody tr"); getCoursePickerRows = () => this.getCoursePicker().find(".course-picker-row"); getCoursePickerSearchInput = () => this.getCoursePicker().find("input"); - getCalendarEvents = () => this.getCalendarSection().find(".fhc-calendar-base-grid-line-event"); + 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 { scrollIntoView = false, ...dragOptions } = options; const event = this.getCalendarEventById(eventId); - const eventToDrag = scrollIntoView ? event.scrollIntoView() : event; - return eventToDrag.should("be.visible").focus().drag( + return event.should("be.visible").drag( this.getCalendarPartDropTarget(partIndex), { waitForAnimations: false, animationDistanceThreshold: 0, - scrollBehavior: false, - ...dragOptions, + ...options, }, ); }; dropCourseOnCalendarPart = (courseIndex, partIndex, options = {}) => { - const { scrollIntoView = false, ...dragOptions } = options; const course = this.getCoursePickerRows().eq(courseIndex); - const courseToDrag = scrollIntoView ? course.scrollIntoView() : course; - return courseToDrag.should("be.visible").drag( + return course.should("be.visible").drag( this.getCalendarPartDropTarget(partIndex), { waitForAnimations: false, animationDistanceThreshold: 0, - ...dragOptions, + ...options, }, ); }; - updateKalenderEvent = (kalenderId, updatedInfos, options = {}) => - cy.request({ - method: "POST", - url: "/index.ci.php/api/frontend/v1/tempus/Kalender/updateKalenderEvent", - body: { - kalender_id: kalenderId, - updatedInfos, - }, - ...options, - }); - restoreKalenderEventTime = (kalenderId, startTime, endTime, options = {}) => - this.updateKalenderEvent( - kalenderId, - { - start_time: startTime, - end_time: endTime, - }, - options, - ); getEventGridRowFromStyle = (style) => /grid-row:\s*([^;]+)/.exec(style)?.[1]?.trim(); getEventGridRow = (id) => @@ -85,10 +114,6 @@ class TempusPage { const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value")); return eventData?.orig?.beginn === startTime; }); - getCalendarEventsByEndTime = (endTime) => this.getCalendarEvents().filter((index, event) => { - const eventData = JSON.parse(event.getAttribute("data-fhc-draggable-value")); - return eventData?.orig?.ende === endTime; - }); 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; @@ -97,6 +122,15 @@ class TempusPage { 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']"); @@ -118,6 +152,16 @@ class TempusPage { this.getPreviewRoleButton(role).click(); }; + disableStundenraster = () => { + this.getStundenrasterToggle().then(($toggle) => { + if ($toggle.find(".fa-toggle-on").length) { + cy.wrap($toggle).click(); + } + }); + + this.getStundenrasterToggle().find("i").should("have.class", "fa-toggle-off"); + }; + waitForCalendarToFinishLoading = () => { this.getCalendarLoadingEvents().should("not.exist"); };