mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender-pwa.git
synced 2025-07-16 09:38:51 +02:00
test:#13 added tests and added licence
This commit is contained in:
@ -1,3 +1,21 @@
|
||||
<!--
|
||||
Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import FullCalendar from "@fullcalendar/vue3";
|
||||
@ -9,7 +27,7 @@ import interactionPlugin from "@fullcalendar/interaction";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
import iCalenderPlugin from "@fullcalendar/icalendar";
|
||||
import router from "@/router";
|
||||
import { formatYearMonthDay } from "@/helpers/dates.ts";
|
||||
import { formatYearMonthDay, removeTZ } from "@/helpers/dates.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import tokenStore from "@/store/tokenStore.ts";
|
||||
@ -29,42 +47,26 @@ const op = ref();
|
||||
const clickedEvent = ref();
|
||||
|
||||
const toggle = (info: EventClickArg) => {
|
||||
const start = !info.event.start ? "" : removeTZ(info.event.start);
|
||||
const end = !info.event.end ? "" : removeTZ(info.event.end);
|
||||
|
||||
if (op.value.visible) {
|
||||
clickedEvent.value = null;
|
||||
op.value.hide();
|
||||
return;
|
||||
} else {
|
||||
|
||||
clickedEvent.value = {
|
||||
title: info.event._def.title,
|
||||
start: start,
|
||||
end: end,
|
||||
notes: info.event._def.extendedProps.notes,
|
||||
allDay: info.event._def.allDay,
|
||||
location: info.event._def.extendedProps.location,
|
||||
};
|
||||
op.value.show(info.jsEvent);
|
||||
op.value.target = info.el;
|
||||
|
||||
const start = info.event.start
|
||||
const end = info.event.end
|
||||
|
||||
if (!start || !end) {
|
||||
clickedEvent.value = {
|
||||
title: info.event._def.title,
|
||||
start: "",
|
||||
end: "",
|
||||
notes: info.event._def.extendedProps.notes,
|
||||
allDay: info.event._def.allDay,
|
||||
location: info.event._def.extendedProps.location,
|
||||
};
|
||||
op.value.show(info.jsEvent);
|
||||
op.value.target = info.el;
|
||||
} else {
|
||||
const timeZoneOffsetStart = start.getTimezoneOffset() * 60000;
|
||||
const timeZoneOffsetEnd = end.getTimezoneOffset() * 60000;
|
||||
|
||||
clickedEvent.value = {
|
||||
title: info.event._def.title,
|
||||
start: new Date(start.getTime() + timeZoneOffsetStart),
|
||||
end: new Date(end.getTime() + timeZoneOffsetEnd),
|
||||
notes: info.event._def.extendedProps.notes,
|
||||
allDay: info.event._def.allDay,
|
||||
location: info.event._def.extendedProps.location,
|
||||
};
|
||||
op.value.show(info.jsEvent);
|
||||
op.value.target = info.el;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,3 +22,8 @@
|
||||
export function formatYearMonthDay(date: Date): string {
|
||||
return date.toISOString().split("T")[0].replace(/-/g, "");
|
||||
}
|
||||
|
||||
|
||||
export function removeTZ(date: Date): Date {
|
||||
return new Date(date.getTime() + date.getTimezoneOffset() * 60000)
|
||||
}
|
141
frontend/src/helpers/ical.test.ts
Normal file
141
frontend/src/helpers/ical.test.ts
Normal file
@ -0,0 +1,141 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import { expect, test } from "vitest";
|
||||
import { exportedForTesting } from "@/helpers/ical.ts";
|
||||
import { CalendarComponent } from "ical";
|
||||
|
||||
|
||||
// colorizeEvents has only the function to colorize the events that are passed to it
|
||||
test("colorizeEventsSameSummary", () => {
|
||||
|
||||
const events: CalendarComponent[] =
|
||||
[
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Operations Research",
|
||||
},
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Operations Research",
|
||||
},
|
||||
];
|
||||
|
||||
expect(exportedForTesting.colorizeEvents(
|
||||
events
|
||||
)
|
||||
).toEqual([{ summary: "Operations Research", color: "var(--htwk-rot-200)" },{ summary: "Operations Research", color: "var(--htwk-rot-200)" }]);
|
||||
}
|
||||
);
|
||||
|
||||
test("colorizeEventsDifferentSummary", () => {
|
||||
|
||||
const events: CalendarComponent[] =
|
||||
[
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Algorithmische Mathematik",
|
||||
},
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Funktionale Programmierung",
|
||||
},
|
||||
];
|
||||
|
||||
expect(exportedForTesting.colorizeEvents(
|
||||
events
|
||||
)
|
||||
).toEqual([{ summary: "Algorithmische Mathematik", color: "var(--htwk-rot-200)" },{ summary: "Funktionale Programmierung", color: "var(--htwk-gruen-300)" }]);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
test("filterEventsDistinct", () => {
|
||||
const events: CalendarComponent[] =
|
||||
[
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Operations Research",
|
||||
},
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Operations Research",
|
||||
},
|
||||
];
|
||||
|
||||
expect(exportedForTesting.filterEventsDistinct(
|
||||
events
|
||||
)
|
||||
).toEqual([{ type: "VEVENT", summary: "Operations Research" }]);
|
||||
});
|
||||
|
||||
test("filterEventsDistinctDifferentSummary", () => {
|
||||
const events: CalendarComponent[] =
|
||||
[
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Algorithmische Mathematik",
|
||||
},
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Funktionale Programmierung",
|
||||
},
|
||||
];
|
||||
|
||||
expect(exportedForTesting.filterEventsDistinct(
|
||||
events
|
||||
)
|
||||
).toEqual(events);
|
||||
});
|
||||
|
||||
|
||||
test("extractedColorizedEvents", () => {
|
||||
const events: CalendarComponent[] =
|
||||
[
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Operations Research",
|
||||
},
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Operations Research",
|
||||
},
|
||||
];
|
||||
|
||||
expect(exportedForTesting.extractedColorizedEvents(
|
||||
events
|
||||
)
|
||||
).toEqual([{ summary: "Operations Research", color: "var(--htwk-rot-200)" }]);
|
||||
});
|
||||
|
||||
test("extractedColorizedEventsDifferentSummary", () => {
|
||||
const events: CalendarComponent[] =
|
||||
[
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Algorithmische Mathematik",
|
||||
},
|
||||
{
|
||||
type: "VEVENT",
|
||||
summary: "Funktionale Programmierung",
|
||||
},
|
||||
];
|
||||
|
||||
expect(exportedForTesting.extractedColorizedEvents(
|
||||
events
|
||||
)
|
||||
).toEqual([{ summary: "Algorithmische Mathematik", color: "var(--htwk-rot-200)" },{ summary: "Funktionale Programmierung", color: "var(--htwk-gruen-300)" }]);
|
||||
});
|
@ -1,3 +1,19 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import ICAL from "ical.js";
|
||||
import { CalendarComponent } from "ical";
|
||||
|
||||
@ -7,7 +23,7 @@ import { CalendarComponent } from "ical";
|
||||
* @param color Color code for the event
|
||||
*/
|
||||
export interface ColorDistinctionEvent {
|
||||
title: string;
|
||||
summary: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
@ -23,7 +39,7 @@ export function parseICalData(icalData: string | undefined): CalendarComponent[]
|
||||
const jCalData = ICAL.parse(icalData);
|
||||
const comp = new ICAL.Component(jCalData);
|
||||
const vEvents = comp.getAllSubcomponents('vevent');
|
||||
const colorDistinctionEvents = extracted(vEvents);
|
||||
const colorDistinctionEvents = extractedColorizedEvents(vEvents);
|
||||
|
||||
return vEvents.map((vevent: CalendarComponent) => {
|
||||
const event = new ICAL.Event(vevent);
|
||||
@ -34,7 +50,7 @@ export function parseICalData(icalData: string | undefined): CalendarComponent[]
|
||||
end: event.endDate.toJSDate(),
|
||||
notes: event.description,
|
||||
allDay: event.startDate.isDate,
|
||||
color: colorDistinctionEvents.find((e: ColorDistinctionEvent) => e.title === event.summary)?.color,
|
||||
color: colorDistinctionEvents.find((e: ColorDistinctionEvent) => e.summary === event.summary)?.color,
|
||||
id: event.uid,
|
||||
location: event.location,
|
||||
};
|
||||
@ -47,21 +63,33 @@ export function parseICalData(icalData: string | undefined): CalendarComponent[]
|
||||
* @param vEvents Array of calendar components
|
||||
* @returns Array of objects with event name and color
|
||||
*/
|
||||
function extracted(vEvents: CalendarComponent[]): ColorDistinctionEvent[] {
|
||||
// filter vevents to get all unique event names
|
||||
const distinctEvents = vEvents.filter((vevent: CalendarComponent, index: number, self: CalendarComponent[]) => {
|
||||
const event = new ICAL.Event(vevent);
|
||||
function extractedColorizedEvents(vEvents: CalendarComponent[]): ColorDistinctionEvent[] {
|
||||
return colorizeEvents(filterEventsDistinct(vEvents));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters out duplicate events
|
||||
* @param vEvents Array of calendar components
|
||||
* @returns Array of calendar components without duplicates
|
||||
*/
|
||||
function filterEventsDistinct(vEvents: CalendarComponent[]): CalendarComponent[] {
|
||||
return vEvents.filter((vevent: CalendarComponent, index: number, self: CalendarComponent[]) => {
|
||||
return self.findIndex((v) => {
|
||||
const e = new ICAL.Event(v);
|
||||
return e.summary === event.summary;
|
||||
return v.summary === vevent.summary;
|
||||
}) === index;
|
||||
});
|
||||
}
|
||||
|
||||
//create dynamic color distinctionEvents map that will be used to color code events
|
||||
return distinctEvents.map((vevent: CalendarComponent) => {
|
||||
const event = new ICAL.Event(vevent);
|
||||
// map event name to a color for color coding css
|
||||
// like var(--htwk-rot-700)
|
||||
|
||||
/**
|
||||
* Assigns a color to each event
|
||||
* @param vEvents Array of calendar components
|
||||
* @returns Array of objects with event name and color
|
||||
*/
|
||||
function colorizeEvents(vEvents: CalendarComponent[]): ColorDistinctionEvent[] {
|
||||
|
||||
return vEvents.map((vevent: CalendarComponent) => {
|
||||
const colors: string[] = [
|
||||
"var(--htwk-rot-200)",
|
||||
"var(--htwk-gruen-300)",
|
||||
@ -76,14 +104,19 @@ function extracted(vEvents: CalendarComponent[]): ColorDistinctionEvent[] {
|
||||
"var(--htwk-blau-200)"
|
||||
];
|
||||
|
||||
// give each event a color based on its name ascending
|
||||
const randomColor = colors[distinctEvents.findIndex((e: CalendarComponent) => {
|
||||
const ev = new ICAL.Event(e);
|
||||
return ev.summary === event.summary;
|
||||
const randomColor = colors[vEvents.findIndex((e: CalendarComponent) => {
|
||||
return e.summary === vevent.summary?? "";
|
||||
}) % colors.length];
|
||||
|
||||
return {
|
||||
title: event.summary,
|
||||
summary: vevent.summary?? "",
|
||||
color: randomColor
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export const exportedForTesting = {
|
||||
extractedColorizedEvents,
|
||||
filterEventsDistinct,
|
||||
colorizeEvents
|
||||
};
|
@ -1,3 +1,19 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const tokenRegex = /^[a-z0-9]{15}$/;
|
||||
const tokenUriRegex = /[?&]token=([a-z0-9]{15})(?:&|$)/;
|
||||
|
||||
|
Reference in New Issue
Block a user