diff --git a/src/main/webapp/app/lti/lti13-deep-linking.component.html b/src/main/webapp/app/lti/lti13-deep-linking.component.html
index 4c49a88340b7..a3964e4ac713 100644
--- a/src/main/webapp/app/lti/lti13-deep-linking.component.html
+++ b/src/main/webapp/app/lti/lti13-deep-linking.component.html
@@ -75,9 +75,15 @@
diff --git a/src/main/webapp/app/lti/lti13-deep-linking.component.ts b/src/main/webapp/app/lti/lti13-deep-linking.component.ts
index 365c86fd3350..1c8e7e0d7b85 100644
--- a/src/main/webapp/app/lti/lti13-deep-linking.component.ts
+++ b/src/main/webapp/app/lti/lti13-deep-linking.component.ts
@@ -130,7 +130,7 @@ export class Lti13DeepLinkingComponent implements OnInit {
* If an exercise is selected, it sends a POST request to initiate deep linking.
*/
sendDeepLinkRequest() {
- if (this.selectedExercises) {
+ if (this.selectedExercises?.size) {
const ltiIdToken = this.sessionStorageService.retrieve('ltiIdToken') ?? '';
const clientRegistrationId = this.sessionStorageService.retrieve('clientRegistrationId') ?? '';
const exerciseIds = Array.from(this.selectedExercises).join(',');
@@ -154,6 +154,8 @@ export class Lti13DeepLinkingComponent implements OnInit {
onError(this.alertService, error);
},
});
+ } else {
+ this.alertService.error('artemisApp.lti13.deepLinking.selectToLink');
}
}
}
diff --git a/src/main/webapp/i18n/de/lti.json b/src/main/webapp/i18n/de/lti.json
index e8b95943d1df..102afd4fd241 100644
--- a/src/main/webapp/i18n/de/lti.json
+++ b/src/main/webapp/i18n/de/lti.json
@@ -87,6 +87,7 @@
"linkedSuccessfully": "Verknüpfung der Übungen erfolgreich",
"linkedFailed": "Fehler beim Deep-Linking",
"link": "Verlinken",
+ "selectToLink": "Übungen zum Verlinken auswählen",
"unknownError": "Aufgrund eines unbekannten Fehlers nicht erfolgreich. Bitte kontaktiere einen Admin!",
"noCoursesError": "Keiner deiner Artemis-Kurse hat LTI aktiviert. Bitte fahre mit der Konfiguration mindestens eines Kurses fort.",
"learnMore": "Erfahre mehr über die Einrichtung von LTI in einem Kurs.."
diff --git a/src/main/webapp/i18n/en/lti.json b/src/main/webapp/i18n/en/lti.json
index 482d15e9722f..47037969f583 100644
--- a/src/main/webapp/i18n/en/lti.json
+++ b/src/main/webapp/i18n/en/lti.json
@@ -87,6 +87,7 @@
"linkedSuccessfully": "Linked exercises successfully",
"linkedFailed": "Error during deep linking",
"link": "Link",
+ "selectToLink": "Select exercises to link",
"unknownError": "Unsuccessful due to an unknown error. Please contact an admin!",
"noCoursesError": "You do not have any LTI-enabled courses in Artemis yet. Please configure at least one course to use this feature.",
"learnMore": "Learn more about enabling LTI in your course.."
diff --git a/src/test/javascript/spec/component/lti/lti13-deep-linking.component.spec.ts b/src/test/javascript/spec/component/lti/lti13-deep-linking.component.spec.ts
index 03f30f0f2de1..69e22cff2c96 100644
--- a/src/test/javascript/spec/component/lti/lti13-deep-linking.component.spec.ts
+++ b/src/test/javascript/spec/component/lti/lti13-deep-linking.component.spec.ts
@@ -7,7 +7,7 @@ import { AccountService } from 'app/core/auth/account.service';
import { SortService } from 'app/shared/service/sort.service';
import { of, throwError } from 'rxjs';
import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
-import { MockPipe, MockProvider } from 'ng-mocks';
+import { MockPipe } from 'ng-mocks';
import { User } from 'app/core/user/user.model';
import { Course } from 'app/entities/course.model';
import { Exercise, ExerciseType } from 'app/entities/exercise.model';
@@ -27,6 +27,7 @@ describe('Lti13DeepLinkingComponent', () => {
const courseManagementServiceMock = { findWithExercises: jest.fn() };
const accountServiceMock = { identity: jest.fn(), getAuthenticationState: jest.fn() };
const sortServiceMock = { sortByProperty: jest.fn() };
+ const alertServiceMock = { error: jest.fn(), addAlert: jest.fn() };
const exercise1 = { id: 1, shortName: 'git', type: ExerciseType.PROGRAMMING } as Exercise;
const exercise2 = { id: 2, shortName: 'test', type: ExerciseType.PROGRAMMING } as Exercise;
@@ -46,7 +47,7 @@ describe('Lti13DeepLinkingComponent', () => {
{ provide: AccountService, useValue: accountServiceMock },
{ provide: SortService, useValue: sortServiceMock },
{ provide: SessionStorageService, useClass: MockSyncStorage },
- MockProvider(AlertService),
+ { provide: AlertService, useValue: alertServiceMock },
],
}).compileComponents();
jest.spyOn(console, 'error').mockImplementation(() => {});
@@ -76,6 +77,16 @@ describe('Lti13DeepLinkingComponent', () => {
expect(component.exercises).toContainAllValues(course.exercises!);
}));
+ it('should alert user when no exercise is selected', () => {
+ component.selectedExercises = undefined;
+ component.sendDeepLinkRequest();
+ expect(alertServiceMock.error).toHaveBeenCalledWith('artemisApp.lti13.deepLinking.selectToLink');
+
+ component.selectedExercises = new Set();
+ component.sendDeepLinkRequest();
+ expect(alertServiceMock.error).toHaveBeenNthCalledWith(2, 'artemisApp.lti13.deepLinking.selectToLink');
+ });
+
it('should navigate on init when user is authenticated', fakeAsync(() => {
const redirectSpy = jest.spyOn(component, 'redirectUserToLoginThenTargetLink');
accountServiceMock.identity.mockResolvedValue(undefined);