Skip to content
Merged
Show file tree
Hide file tree
Changes from 97 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
634156e
first clean up steps in course-tutorial-groups component
marlonnienaber Oct 22, 2025
568d4e5
finish course-tutorial-groups component clean up
marlonnienaber Oct 22, 2025
27f1b52
add tutorial-lectures to sidebar
marlonnienaber Oct 23, 2025
4f3393e
display lectures when clicked on in sidebar
marlonnienaber Oct 24, 2025
27a924c
add isTutorialLecture property to Lecture
marlonnienaber Oct 26, 2025
0e99e14
merge develop
marlonnienaber Oct 26, 2025
5ea3a07
fix problems when rendering tutorial lectures
marlonnienaber Oct 28, 2025
b777f75
use new accordions
marlonnienaber Oct 28, 2025
229322c
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Oct 28, 2025
3313118
fix bug
marlonnienaber Nov 2, 2025
2c3e52c
improve naming
marlonnienaber Nov 2, 2025
8a05a74
only display tutorial lectures in tutorial group tab
marlonnienaber Nov 2, 2025
ba0f1e7
add tutorial lectzre support to lectzre update components | optimize …
marlonnienaber Nov 3, 2025
41a72fe
serialize isTutorialLecture property correctly
marlonnienaber Nov 3, 2025
dee295f
add javadoc to new endpoints
marlonnienaber Nov 3, 2025
1c66eb6
fix some client test problems
marlonnienaber Nov 3, 2025
c9f6897
fix bug
marlonnienaber Nov 4, 2025
bb8bbf9
write messy client tests
marlonnienaber Nov 4, 2025
103c1c8
add link from tutorial group to current lesson
marlonnienaber Nov 5, 2025
eace6d0
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Nov 5, 2025
dd44f39
fix merge artifact
marlonnienaber Nov 5, 2025
28d4126
hide tutorial lecture communication
marlonnienaber Nov 5, 2025
7f7f3c5
fix bug
marlonnienaber Nov 6, 2025
4f1226f
adapt current lecture logic
marlonnienaber Nov 6, 2025
d2bdee0
remove long lecture warning from single lecture update page
marlonnienaber Nov 6, 2025
2637f3b
remove long lecture warning from lecture series create page
marlonnienaber Nov 6, 2025
75df36b
remove obsolete lecture-period-component test
marlonnienaber Nov 6, 2025
73b1f22
fix lecture service test
marlonnienaber Nov 6, 2025
f8afe34
fix edit-prerequisite component test
marlonnienaber Nov 6, 2025
6949d19
fix sidebar-card-medium component test
marlonnienaber Nov 6, 2025
a1ba8e7
fix course-tutorial-group-detail component tests
marlonnienaber Nov 6, 2025
5e55df4
extend course-overview service tests
marlonnienaber Nov 6, 2025
1bce168
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Nov 6, 2025
c6377b5
fix server test compilation error
marlonnienaber Nov 6, 2025
cd8831d
revert unnecessary changes
marlonnienaber Nov 6, 2025
3e55c2e
add TODOs
marlonnienaber Nov 6, 2025
971be37
remove obsolete LectureResource test | add LectureResource test TODO
marlonnienaber Nov 6, 2025
34e32d3
do TODOs in CourseRepository
marlonnienaber Nov 6, 2025
7bcbbfd
filter out tutorial lectures in atlas student metrics
marlonnienaber Nov 6, 2025
cc5560f
remove completed TODO
marlonnienaber Nov 6, 2025
83420ac
add lecture service tests
marlonnienaber Nov 6, 2025
1fc35cb
add sidebar-card-medium component test
marlonnienaber Nov 6, 2025
5186503
add tests for new endpoints
marlonnienaber Nov 6, 2025
bc61957
add course-tutorial-group-detail component test
marlonnienaber Nov 7, 2025
a77d2ac
add raw type to query parameter
marlonnienaber Nov 7, 2025
ea5f4ee
clean up
marlonnienaber Nov 7, 2025
0e6b79e
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Nov 8, 2025
7bc2d83
fix bug
marlonnienaber Nov 8, 2025
4905fc2
fix client tests
marlonnienaber Nov 8, 2025
0be8b2e
add test to increase coverage
marlonnienaber Nov 8, 2025
532c8d4
increase test coverage
marlonnienaber Nov 8, 2025
7eeb241
introduce allTutorialLectures accordion
marlonnienaber Nov 8, 2025
82d33a3
apply coderabbits suggestions
marlonnienaber Nov 8, 2025
b7e83c5
rename NonTutorialLectures into NormalLectures
marlonnienaber Nov 12, 2025
fa6f65f
reuse mapToLecture instead of using mapToTutorialLecture
marlonnienaber Nov 12, 2025
d2bdb4a
remove unnecessary button around info icon
marlonnienaber Nov 12, 2025
d9fd67c
simplify conditions according to Ramona's CRs
marlonnienaber Nov 12, 2025
1ef840a
apply Yassine's CRs
marlonnienaber Nov 12, 2025
e425fe3
fix breadcrumbs
marlonnienaber Nov 12, 2025
01976a9
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Nov 12, 2025
668214a
apply Ramona's suggestions
marlonnienaber Nov 12, 2025
7373344
Merge remote-tracking branch 'origin/feature/tutorial-groups/tutorial…
marlonnienaber Nov 12, 2025
acf0403
fix failing server test
marlonnienaber Nov 12, 2025
8267bd6
update javadoc
marlonnienaber Nov 12, 2025
bd7d2b3
fix query
marlonnienaber Nov 12, 2025
6b9c13c
fix failing client tests
marlonnienaber Nov 13, 2025
872c507
fix unsupported query
marlonnienaber Nov 13, 2025
3c71e43
fix problematic query
marlonnienaber Nov 13, 2025
864f601
fix failing client tests
marlonnienaber Nov 13, 2025
b841781
use all lectures in Iris instead of just non-tutorial-lectures
marlonnienaber Nov 13, 2025
babd121
move method in CourseRepository
marlonnienaber Nov 13, 2025
4bd1cdc
use all lectures with atlas (not only non-tutorial-lectures)
marlonnienaber Nov 13, 2025
bead847
add missing logic to use all lectures in atlas
marlonnienaber Nov 13, 2025
51683d7
revert obsolete change
marlonnienaber Nov 13, 2025
31a8d36
revert obsolete change
marlonnienaber Nov 13, 2025
3a2984c
revert obsolete change
marlonnienaber Nov 13, 2025
c7be153
revert obsolete change
marlonnienaber Nov 13, 2025
663a257
revert obsolete changes
marlonnienaber Nov 13, 2025
9452f4e
revert obsolete change
marlonnienaber Nov 13, 2025
849ce3f
disable iris for tutorial lectures
marlonnienaber Nov 14, 2025
28dcb6a
apply coderabbits CRs
marlonnienaber Nov 14, 2025
a86b58c
adjust coverage
marlonnienaber Nov 14, 2025
1fefdd4
use all lectures in competency components
marlonnienaber Nov 17, 2025
483a4aa
add raw param
marlonnienaber Nov 17, 2025
22553e2
revert obsolete change
marlonnienaber Nov 17, 2025
9c3f54b
correct javaDoc
marlonnienaber Nov 17, 2025
6dba81e
fix failing client tests
marlonnienaber Nov 17, 2025
70b92eb
fix failing client tests
marlonnienaber Nov 17, 2025
0d51624
improve spy naming
marlonnienaber Nov 17, 2025
386db55
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Nov 17, 2025
70b92f6
revert obsolete changes
marlonnienaber Nov 17, 2025
9d69173
fix client lecture service tests
marlonnienaber Nov 17, 2025
3f60391
revert obsolete change
marlonnienaber Nov 17, 2025
9bbed83
fix server tests
marlonnienaber Nov 17, 2025
220ebd1
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Nov 17, 2025
d9672e5
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Nov 19, 2025
460efed
clean up merge artifacts
marlonnienaber Nov 19, 2025
bc96e87
Merge branch 'develop' into feature/tutorial-groups/tutorial-lectures
marlonnienaber Nov 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ public interface ChannelRepository extends ArtemisJpaRepository<Channel, Long> {
@Query("""
SELECT DISTINCT channel
FROM Channel channel
LEFT JOIN channel.conversationParticipants cp
LEFT JOIN channel.conversationParticipants conversationParticipant
LEFT JOIN channel.lecture lecture
WHERE channel.course.id = :courseId
AND (
channel.isCourseWide = TRUE
OR (channel.id = cp.conversation.id AND cp.user.id = :userId))
AND (channel.isCourseWide OR (channel.id = conversationParticipant.conversation.id AND conversationParticipant.user.id = :userId))
AND (lecture IS NULL OR NOT lecture.isTutorialLecture)
ORDER BY channel.name
""")
List<Channel> findChannelsOfUser(@Param("courseId") Long courseId, @Param("userId") Long userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ SELECT COUNT(c) > 0
Optional<Course> findWithEagerExercisesAndExerciseDetailsAndLecturesById(long courseId);

@EntityGraph(type = LOAD, attributePaths = { "lectures", "lectures.lectureUnits", "lectures.attachments" })
Optional<Course> findWithEagerLecturesAndLectureUnitsById(long courseId);
Optional<Course> findWithLecturesAndLectureUnitsAndAttachmentsById(long courseId);

@EntityGraph(type = LOAD, attributePaths = { "organizations", "competencies", "prerequisites", "tutorialGroupsConfiguration", "onlineCourseConfiguration" })
Optional<Course> findForUpdateById(long courseId);
Expand Down Expand Up @@ -430,8 +430,8 @@ default Course findByIdWithLecturesElseThrow(long courseId) {
}

@NonNull
default Course findByIdWithLecturesAndLectureUnitsElseThrow(long courseId) {
return getValueElseThrow(findWithEagerLecturesAndLectureUnitsById(courseId), courseId);
default Course findWithLecturesAndLectureUnitsAndAttachmentsByIdElseThrow(long courseId) {
return getValueElseThrow(findWithLecturesAndLectureUnitsAndAttachmentsById(courseId), courseId);
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ public String deleteLectureFromPyrisDB(List<AttachmentVideoUnit> attachmentVideo
* @return jobToken if the job was created else null
*/
public String addLectureUnitToPyrisDB(AttachmentVideoUnit attachmentVideoUnit) {
if (lectureIngestionEnabled(attachmentVideoUnit.getLecture().getCourse())) {
if (lectureIngestionEnabled(attachmentVideoUnit.getLecture().getCourse()) && !attachmentVideoUnit.getLecture().isTutorialLecture()) {
if ((attachmentVideoUnit.getVideoSource() != null && !attachmentVideoUnit.getVideoSource().isEmpty()) || (attachmentVideoUnit.getAttachment() != null
&& (attachmentVideoUnit.getAttachment().getAttachmentType() == AttachmentType.FILE && attachmentVideoUnit.getAttachment().getLink().endsWith(".pdf")))) {
return executeLectureAdditionWebhook(processAttachmentVideoUnitForUpdate(attachmentVideoUnit), attachmentVideoUnit.getLecture().getCourse());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.domain.DomainObject;
Expand Down Expand Up @@ -61,6 +62,9 @@ public class Lecture extends DomainObject {
@Column(name = "visible_date")
private ZonedDateTime visibleDate;

@Column(name = "is_tutorial_lecture")
private boolean isTutorialLecture;

@OneToMany(mappedBy = "lecture", cascade = CascadeType.REMOVE, orphanRemoval = true)
@JsonIgnoreProperties(value = "lecture", allowSetters = true)
private Set<Attachment> attachments = new HashSet<>();
Expand Down Expand Up @@ -313,4 +317,13 @@ public boolean isVisibleToStudents() {
}
return visibleDate.isBefore(ZonedDateTime.now());
}

@JsonProperty("isTutorialLecture")
public boolean isTutorialLecture() {
return isTutorialLecture;
}

public void setIsTutorialLecture(boolean isTutorialLecture) {
this.isTutorialLecture = isTutorialLecture;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ public interface LectureRepository extends ArtemisJpaRepository<Lecture, Long> {
""")
Set<Lecture> findAllByCourseId(@Param("courseId") Long courseId);

@Query("""
SELECT lecture
FROM Lecture lecture
WHERE lecture.course.id = :courseId AND lecture.id IN :ids
""")
Set<Lecture> findAllByCourseIdWithIdIn(@Param("courseId") long courseId, @Param("ids") Set<Long> ids);

@Query("""
SELECT new de.tum.cit.aet.artemis.core.dto.calendar.LectureCalendarEventDTO(
lecture.id,
Expand All @@ -52,16 +45,15 @@ public interface LectureRepository extends ArtemisJpaRepository<Lecture, Long> {
lecture.endDate
)
FROM Lecture lecture
WHERE lecture.course.id = :courseId AND (lecture.startDate IS NOT NULL OR lecture.endDate IS NOT NULL)
WHERE lecture.course.id = :courseId AND (lecture.startDate IS NOT NULL OR lecture.endDate IS NOT NULL) AND NOT lecture.isTutorialLecture
""")
Set<LectureCalendarEventDTO> getLectureCalendarEventDTOsForCourseId(@Param("courseId") long courseId);

@Query("""
SELECT lecture
FROM Lecture lecture
LEFT JOIN FETCH lecture.lectureUnits
WHERE lecture.course.id = :courseId
AND (lecture.visibleDate IS NULL OR lecture.visibleDate <= :now)
WHERE lecture.course.id = :courseId AND (lecture.visibleDate IS NULL OR lecture.visibleDate <= :now)
""")
Set<Lecture> findAllVisibleByCourseIdWithEagerLectureUnits(@Param("courseId") long courseId, @Param("now") ZonedDateTime now);

Expand All @@ -81,6 +73,13 @@ public interface LectureRepository extends ArtemisJpaRepository<Lecture, Long> {
""")
Set<Lecture> findAllByCourseIdWithAttachments(@Param("courseId") Long courseId);

@Query("""
SELECT lecture
FROM Lecture lecture
WHERE lecture.course.id = :courseId AND lecture.isTutorialLecture
""")
Set<Lecture> findAllTutorialLecturesByCourseId(@Param("courseId") Long courseId);

@Query("""
SELECT lecture
FROM Lecture lecture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,17 @@ public interface LectureUnitMetricsRepository extends ArtemisJpaRepository<Lectu
* @return the lecture unit information for all lecture units in the course
*/
@Query("""
SELECT new de.tum.cit.aet.artemis.atlas.dto.metrics.LectureUnitInformationDTO(lu.id, lu.lecture.id, lu.lecture.title, COALESCE(lu.name, a.name), lu.releaseDate, TYPE(lu))
FROM LectureUnit lu
LEFT JOIN Attachment a ON a.attachmentVideoUnit.id = lu.id
WHERE lu.lecture.course.id = :courseId
SELECT new de.tum.cit.aet.artemis.atlas.dto.metrics.LectureUnitInformationDTO(
lectureUnit.id,
lectureUnit.lecture.id,
lectureUnit.lecture.title,
COALESCE(lectureUnit.name, attachment.name),
lectureUnit.releaseDate,
TYPE(lectureUnit)
)
FROM LectureUnit lectureUnit
LEFT JOIN Attachment attachment ON attachment.attachmentVideoUnit.id = lectureUnit.id
WHERE lectureUnit.lecture.course.id = :courseId
""")
Set<LectureUnitInformationDTO> findAllLectureUnitInformationByCourseId(@Param("courseId") long courseId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,24 @@ public ResponseEntity<Set<Lecture>> getLecturesForCourse(@PathVariable Long cour
return ResponseEntity.ok().body(lectures);
}

/**
* GET /courses/:courseId/tutorial-lectures : get all the tutorial-lectures of a course
*
* @param courseId the courseId of the course for which the lectures should be returned
* @return the ResponseEntity with status 200 (OK) and the list of lectures in body
*/
@GetMapping("courses/{courseId}/tutorial-lectures")
@EnforceAtLeastEditor
public ResponseEntity<Set<Lecture>> getTutorialLecturesForCourse(@PathVariable Long courseId) {
log.debug("REST request to get all Lectures for the course with id : {}", courseId);

Course course = courseRepository.findByIdElseThrow(courseId);
authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null);

Set<Lecture> lectures = lectureRepository.findAllTutorialLecturesByCourseId(courseId);
return ResponseEntity.ok().body(lectures);
}

/**
* GET /courses/:courseId/lectures : get all the lectures of a course with their lecture units and slides
*
Expand Down Expand Up @@ -383,7 +401,7 @@ public ResponseEntity<Lecture> importLecture(@PathVariable long sourceLectureId,
@PostMapping("courses/{courseId}/ingest")
@EnforceAtLeastInstructorInCourse
public ResponseEntity<Void> ingestLectures(@PathVariable Long courseId, @RequestParam(required = false) Optional<Long> lectureId) {
Course course = courseRepository.findByIdWithLecturesAndLectureUnitsElseThrow(courseId);
Course course = courseRepository.findWithLecturesAndLectureUnitsAndAttachmentsByIdElseThrow(courseId);
authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.INSTRUCTOR, course, null);
if (lectureId.isPresent()) {
Optional<Lecture> lectureToIngest = course.getLectures().stream().filter(lecture -> lecture.getId().equals(lectureId.get())).findFirst();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ public ResponseEntity<Void> ingestLectureUnit(@PathVariable long lectureId, @Pat
if (lectureUnit.getLecture().getId() != lectureId) {
throw new BadRequestAlertException("Requested lecture unit is not part of the specified lecture", ENTITY_NAME, "lectureIdMismatch");
}
if (lectureUnit.getLecture().isTutorialLecture()) {
throw new BadRequestAlertException("Units of tutorial lectures can not be ingested", ENTITY_NAME, "tutorialLectureIngestion");
}
if (!(lectureUnit instanceof AttachmentVideoUnit)) {
throw new BadRequestAlertException("Only attachment video units can be ingested into Pyris", ENTITY_NAME, "invalidLectureUnitType");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

<changeSet id="20251024184500-add-is-tutorial-lecture-column" author="marlonnienaber">
<addColumn tableName="lecture">
<column name="is_tutorial_lecture" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>

<rollback>
<dropColumn tableName="lecture" columnName="is_tutorial_lecture"/>
</rollback>
</changeSet>

</databaseChangeLog>
5 changes: 3 additions & 2 deletions src/main/resources/config/liquibase/master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@
<include file="classpath:config/liquibase/changelog/20250911170100_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20250920163252_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20250930145942_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251017200800_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251012122800_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251023184348_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251017200800_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251020175436_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251023184348_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251024184500_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251106150715_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20251113185215_changelog.xml" relativeToChangelogFile="false"/>
<!-- NOTE: please use the format "YYYYMMDDhhmmss_changelog.xml", i.e. year month day hour minutes seconds and not something else! -->
Expand Down
11 changes: 11 additions & 0 deletions src/main/webapp/app/core/course/overview/courses.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ export const courseRoutes: Routes = [
},
canActivate: [UserRouteAccessService, CourseOverviewGuard],
children: [
{
path: 'tutorial-lectures/:lectureId',
data: {
authorities: [Authority.USER],
pageTitle: 'overview.lectures',
hasSidebar: true,
showRefreshButton: true,
},
canActivate: [UserRouteAccessService],
loadComponent: () => import('app/lecture/overview/course-lectures/details/course-lecture-details.component').then((m) => m.CourseLectureDetailsComponent),
},
{
path: ':tutorialGroupId',
loadComponent: () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,33 +189,122 @@ describe('CourseOverviewService', () => {
expect(groupedLectures['current'].entityData[0].title).toBe('Algorithms');
});

it('should return undefined if lectures array is undefined', () => {
expect(courseOverviewService.getUpcomingLecture(undefined)).toBeUndefined();
describe('getUpcomingLecture', () => {
it('should return undefined if lectures array is undefined', () => {
expect(courseOverviewService.getUpcomingLecture(undefined)).toBeUndefined();
});

it('should return undefined if lectures array is empty', () => {
expect(courseOverviewService.getUpcomingLecture([])).toBeUndefined();
});

it('should handle all past lectures', () => {
const pastLectures: Lecture[] = [
{ id: 1, title: 'Past Lecture 1', startDate: dayjs().subtract(2, 'day') },
{ id: 2, title: 'Past Lecture 2', startDate: dayjs().subtract(1, 'day') },
];
const upcomingLecture = courseOverviewService.getUpcomingLecture(pastLectures);

expect(upcomingLecture?.id).toBe(2);
});

it('should correctly identify the lecture furthest in the future', () => {
const lectures: Lecture[] = [
{ id: 1, title: 'Past Lecture', startDate: dayjs().subtract(1, 'day') },
{ id: 2, title: 'Upcoming Lecture', startDate: dayjs().add(1, 'day') },
{ id: 3, title: 'Far Future Lecture', startDate: dayjs().add(2, 'weeks') },
];
const upcomingLecture = courseOverviewService.getUpcomingLecture(lectures);

expect(upcomingLecture?.id).toBe(3);
});
});

it('should return undefined if lectures array is empty', () => {
expect(courseOverviewService.getUpcomingLecture([])).toBeUndefined();
describe('getUpcomingTutorialGroup', () => {
it('should return undefined if tutorial groups are undefined', () => {
const result = courseOverviewService.getUpcomingTutorialGroup(undefined);
expect(result).toBeUndefined();
});

it('should return undefined if there are no future tutorial groups', () => {
const now = dayjs();
const futureTutorialGroups: TutorialGroup[] = [
{ id: 1, nextSession: { start: now.subtract(1, 'day') } },
{ id: 2, nextSession: { start: now.subtract(2, 'day') } },
];
const result = courseOverviewService.getUpcomingTutorialGroup(futureTutorialGroups);
expect(result).toBeUndefined();
});

it('should return upcoming of tutorial groups', () => {
const now = dayjs();
const futureTutorialGroups: TutorialGroup[] = [
{ id: 1, nextSession: { start: now.add(1, 'day') } },
{ id: 2, nextSession: { start: now.add(2, 'day') } },
];
const result = courseOverviewService.getUpcomingTutorialGroup(futureTutorialGroups);
expect(result?.id).toBe(1);
});
});

it('should handle all past lectures', () => {
const pastLectures: Lecture[] = [
{ id: 1, title: 'Past Lecture 1', startDate: dayjs().subtract(2, 'day') },
{ id: 2, title: 'Past Lecture 2', startDate: dayjs().subtract(1, 'day') },
it('should map lectures correctly to sidebar card elements', () => {
const translateService = TestBed.inject(TranslateService);
jest.spyOn(translateService, 'instant').mockReturnValue('No Date');
const firstLectureStart = dayjs('2025-01-01T00:00:00Z');
const lectures: Lecture[] = [
{ id: 1, title: 'Lecture 1', startDate: dayjs('2025-01-01T00:00:00Z') },
{ id: 2, title: 'Lecture 2' },
];
const upcomingLecture = courseOverviewService.getUpcomingLecture(pastLectures);

expect(upcomingLecture?.id).toBe(2);
const result = courseOverviewService.mapLecturesToSidebarCardElements(lectures);

expect(result).toEqual([
{
title: 'Lecture 1',
id: 1,
subtitleLeft: firstLectureStart.format('MMM DD, YYYY'),
size: 'M',
startDate: firstLectureStart,
},
{
title: 'Lecture 2',
id: 2,
subtitleLeft: 'No Date',
size: 'M',
startDate: undefined,
},
]);
});

it('should correctly identify the lecture furthest in the future', () => {
it('should map tutorial lectures correctly to sidebar card elements', () => {
const translateService = TestBed.inject(TranslateService);
jest.spyOn(translateService, 'instant').mockReturnValue('No Date');
const firstLectureStart = dayjs('2025-01-01T00:00:00Z');
const lectures: Lecture[] = [
{ id: 1, title: 'Past Lecture', startDate: dayjs().subtract(1, 'day') },
{ id: 2, title: 'Upcoming Lecture', startDate: dayjs().add(1, 'day') },
{ id: 3, title: 'Far Future Lecture', startDate: dayjs().add(2, 'weeks') },
{ id: 1, title: 'Lecture 1', startDate: dayjs('2025-01-01T00:00:00Z'), isTutorialLecture: true },
{ id: 2, title: 'Lecture 2', isTutorialLecture: true },
];
const upcomingLecture = courseOverviewService.getUpcomingLecture(lectures);

expect(upcomingLecture?.id).toBe(3);
const result = courseOverviewService.mapLecturesToSidebarCardElements(lectures);

expect(result).toEqual([
{
title: 'Lecture 1',
id: 1,
targetComponentSubRoute: 'tutorial-lectures',
subtitleLeft: firstLectureStart.format('MMM DD, YYYY'),
size: 'M',
startDate: firstLectureStart,
},
{
title: 'Lecture 2',
id: 2,
targetComponentSubRoute: 'tutorial-lectures',
subtitleLeft: 'No Date',
size: 'M',
startDate: undefined,
},
]);
});

it('should group exercises by start date and map to sidebar card elements', () => {
Expand Down
Loading
Loading