diff --git a/src/components/feature-board-file-element/FileContentElement.unit.ts b/src/components/feature-board-file-element/FileContentElement.unit.ts
index 0aecf000af..80742e1dac 100644
--- a/src/components/feature-board-file-element/FileContentElement.unit.ts
+++ b/src/components/feature-board-file-element/FileContentElement.unit.ts
@@ -1,4 +1,8 @@
-import { FileRecordScanStatus, PreviewStatus } from "@/fileStorageApi/v3";
+import {
+ FileRecordScanStatus,
+ PreviewStatus,
+ PreviewWidth,
+} from "@/fileStorageApi/v3";
import NotifierModule from "@/store/notifier";
import { AnyContentElement } from "@/types/board/ContentElement";
import { convertDownloadToPreviewUrl } from "@/utils/fileHelper";
@@ -10,8 +14,8 @@ import { fileElementResponseFactory } from "@@/tests/test-utils/factory/fileElem
import { fileRecordResponseFactory } from "@@/tests/test-utils/factory/filerecordResponse.factory";
import { MountOptions, shallowMount } from "@vue/test-utils";
import Vue from "vue";
-import FileContent from "./content/FileContent.vue";
import FileContentElement from "./FileContentElement.vue";
+import FileContent from "./content/FileContent.vue";
import { FileProperties } from "./shared/types/file-properties";
import FileUpload from "./upload/FileUpload.vue";
@@ -131,7 +135,10 @@ describe("FileContentElement", () => {
name: fileRecordResponse.name,
isDownloadAllowed: true,
url: fileRecordResponse.url,
- previewUrl: convertDownloadToPreviewUrl(fileRecordResponse.url),
+ previewUrl: convertDownloadToPreviewUrl(
+ fileRecordResponse.url,
+ PreviewWidth._500
+ ),
size: fileRecordResponse.size,
previewStatus: fileRecordResponse.previewStatus,
element,
@@ -441,7 +448,10 @@ describe("FileContentElement", () => {
name: fileRecordResponse.name,
isDownloadAllowed: true,
url: fileRecordResponse.url,
- previewUrl: convertDownloadToPreviewUrl(fileRecordResponse.url),
+ previewUrl: convertDownloadToPreviewUrl(
+ fileRecordResponse.url,
+ PreviewWidth._500
+ ),
size: fileRecordResponse.size,
previewStatus: fileRecordResponse.previewStatus,
element,
diff --git a/src/components/feature-board-file-element/FileContentElement.vue b/src/components/feature-board-file-element/FileContentElement.vue
index 41bd32e0b7..7ce005a3c0 100644
--- a/src/components/feature-board-file-element/FileContentElement.vue
+++ b/src/components/feature-board-file-element/FileContentElement.vue
@@ -35,7 +35,7 @@
diff --git a/src/components/feature-date-time-picker/DateTimePicker.unit.ts b/src/components/feature-date-time-picker/DateTimePicker.unit.ts
index ade22cfb68..8f57a6d2c9 100644
--- a/src/components/feature-date-time-picker/DateTimePicker.unit.ts
+++ b/src/components/feature-date-time-picker/DateTimePicker.unit.ts
@@ -36,46 +36,65 @@ describe("DateTimePicker", () => {
expect(wrapper.findComponent(DateTimePicker).exists()).toBe(true);
});
- it("should emit input event on date input", async () => {
- setup({ dateTime: new Date().toISOString() });
+ describe("if date and time are set", () => {
+ it("should emit input event on date input", async () => {
+ setup({ dateTime: new Date().toISOString() });
- const datePicker = wrapper.findComponent({ name: "date-picker" });
- expect(datePicker.exists()).toBe(true);
- const tomorrow = new Date();
- tomorrow.setDate(tomorrow.getDate() + 1);
- datePicker.vm.$emit("update:date", tomorrow);
+ const datePicker = wrapper.findComponent({ name: "date-picker" });
+ expect(datePicker.exists()).toBe(true);
+ const tomorrow = new Date();
+ tomorrow.setDate(tomorrow.getDate() + 1);
+ datePicker.vm.$emit("update:date", tomorrow);
- await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
- expect(wrapper.emitted("input")).toHaveLength(1);
- });
+ expect(wrapper.emitted("input")).toHaveLength(1);
+ });
- it("should emit input event on time input", async () => {
- jest.useFakeTimers();
- setup({ dateTime: new Date().toISOString() });
+ it("should emit input event on time input", async () => {
+ jest.useFakeTimers();
+ setup({ dateTime: new Date().toISOString() });
- const timePicker = wrapper.findComponent({ name: "time-picker" });
- expect(timePicker.exists()).toBe(true);
- timePicker.vm.$emit("update:time", "00:00");
+ const timePicker = wrapper.findComponent({ name: "time-picker" });
+ expect(timePicker.exists()).toBe(true);
+ timePicker.vm.$emit("update:time", "00:00");
- jest.advanceTimersByTime(1000);
+ jest.advanceTimersByTime(1000);
- expect(wrapper.emitted("input")).toHaveLength(1);
+ expect(wrapper.emitted("input")).toHaveLength(1);
+ });
});
- it("should restrict timepicker when date is today", async () => {
- setup({ dateTime: new Date().toISOString() });
+ describe("if only date is set", () => {
+ it("should emit input event with time set to 23:59", async () => {
+ jest.useFakeTimers();
+ setup({ dateTime: "" });
- const timePicker = wrapper.findComponent({ name: "time-picker" });
- expect(timePicker.exists()).toBe(true);
- expect(timePicker.props("allowPast")).toBe(false);
+ const datePicker = wrapper.findComponent({ name: "date-picker" });
+ const date = new Date("2030-01-01");
+ datePicker.vm.$emit("update:date", date);
+
+ jest.advanceTimersByTime(1000);
+
+ const emits = wrapper.emitted("input");
+ expect(emits?.length).toEqual(1);
+ date.setHours(23);
+ date.setMinutes(59);
+ expect(emits?.[0]).toEqual([date.toISOString()]);
+ });
});
- it("should not restrict timepicker when date is in the future", async () => {
- setup({ dateTime: new Date("2300-01-01T00:00:00").toISOString() });
+ describe("if only time is set", () => {
+ it("should emit no input event", async () => {
+ jest.useFakeTimers();
+ setup({ dateTime: "" });
- const timePicker = wrapper.findComponent({ name: "time-picker" });
- expect(timePicker.exists()).toBe(true);
- expect(timePicker.props("allowPast")).toBe(true);
+ const timePicker = wrapper.findComponent({ name: "time-picker" });
+ timePicker.vm.$emit("update:time", "00:00");
+
+ jest.advanceTimersByTime(1000);
+
+ expect(wrapper.emitted("input")).toBe(undefined);
+ });
});
});
diff --git a/src/components/feature-date-time-picker/DateTimePicker.vue b/src/components/feature-date-time-picker/DateTimePicker.vue
index 16b41671b1..efb5604181 100644
--- a/src/components/feature-date-time-picker/DateTimePicker.vue
+++ b/src/components/feature-date-time-picker/DateTimePicker.vue
@@ -2,22 +2,21 @@
@@ -26,9 +25,8 @@
import DatePicker from "./DatePicker.vue";
import TimePicker from "./TimePicker.vue";
import { useVModel } from "@vueuse/core";
-import { isToday } from "@/plugins/datetime";
-import { I18N_KEY, injectStrict } from "@/utils/inject";
-import { defineComponent, ref } from "vue";
+import { computed, defineComponent, ref } from "vue";
+import { useI18n } from "@/composables/i18n.composable";
import dayjs from "dayjs";
export default defineComponent({
@@ -48,15 +46,12 @@ export default defineComponent({
timeInputAriaLabel: { type: String, default: "" },
minDate: { type: String },
maxDate: { type: String },
- required: {
- type: Boolean,
- },
- allowPast: { type: Boolean },
},
emits: ["input"],
setup(props, { emit }) {
- const i18n = injectStrict(I18N_KEY);
- const locale = i18n.locale;
+ const { locale } = useI18n();
+
+ const dateTimeInPast = ref(false);
const getTime = (dateIsoString: string) => {
if (dateIsoString === "") {
@@ -73,39 +68,45 @@ export default defineComponent({
dateTime.value ? dayjs(dateTime.value).format("YYYY-MM-DD") : ""
);
const time = ref(dateTime.value ? getTime(dateTime.value) : "");
- const dateIsToday = ref(isToday(date.value));
+ const dateRequired = computed(() => time.value !== "");
const emitDateTime = () => {
- if (date.value !== "" && time.value !== "") {
- const dateTime = new Date(date.value);
- const hoursAndMinutes = time.value.split(":");
- dateTime.setHours(
- parseInt(hoursAndMinutes[0]),
- parseInt(hoursAndMinutes[1])
- );
- emit("input", dateTime.toISOString());
+ if (date.value === "") {
+ return;
+ }
+
+ let timeValue = time.value;
+ if (timeValue === "") {
+ timeValue = "23:59";
}
+
+ const dateTime = new Date(date.value);
+ const hoursAndMinutes = timeValue.split(":");
+ dateTime.setHours(
+ parseInt(hoursAndMinutes[0]),
+ parseInt(hoursAndMinutes[1])
+ );
+ dateTimeInPast.value = dateTime < new Date();
+ emit("input", dateTime.toISOString());
};
- const handleDateInput = (newDate: string) => {
+ const onDateUpdate = (newDate: string) => {
date.value = newDate;
- dateIsToday.value = isToday(date.value);
-
emitDateTime();
};
- const handleTimeInput = (newTime: string) => {
+ const onTimeUpdate = (newTime: string) => {
time.value = newTime;
-
emitDateTime();
};
return {
date,
time,
- dateIsToday,
- handleDateInput,
- handleTimeInput,
+ onDateUpdate,
+ onTimeUpdate,
+ dateRequired,
+ dateTimeInPast,
};
},
});
diff --git a/src/components/feature-date-time-picker/TimePicker.unit.ts b/src/components/feature-date-time-picker/TimePicker.unit.ts
index 7b947d6a42..721b4adc86 100644
--- a/src/components/feature-date-time-picker/TimePicker.unit.ts
+++ b/src/components/feature-date-time-picker/TimePicker.unit.ts
@@ -144,108 +144,5 @@ describe("TimePicker", () => {
expect(wrapper.emitted("update:time")).toBeUndefined();
});
});
-
- describe("when time in the past is not allowed", () => {
- it("should disable selection of time in the past", async () => {
- setup({
- time: "12:30",
- allowPast: false,
- });
-
- const textField = wrapper.findComponent({ name: "v-text-field" });
- const input = textField.find("input");
- expect(input.exists()).toBe(true);
- await input.trigger("click");
-
- const oneOClockListItem = wrapper
- .findAll({ name: "v-list-item" })
- .at(0); // 00:00
- expect(oneOClockListItem.attributes()["aria-disabled"]).toBeDefined();
- });
-
- it("should enable selection of time in the future", async () => {
- setup({
- time: "12:30",
- allowPast: false,
- });
-
- const textField = wrapper.findComponent({ name: "v-text-field" });
- const input = textField.find("input");
- expect(input.exists()).toBe(true);
- await input.trigger("click");
-
- const fiveOClockListItem = wrapper
- .findAll({ name: "v-list-item" })
- .at(10); // 05:00
- expect(
- fiveOClockListItem.attributes()["aria-disabled"]
- ).toBeUndefined();
- });
-
- it("should not emit update:time event when time inserted is in the past", async () => {
- setup({
- time: "12:30",
- allowPast: false,
- });
-
- const input = wrapper
- .findComponent({ name: "v-text-field" })
- .find("input");
-
- await input.trigger("focus");
- await input.setValue("03:01");
- await input.trigger("blur");
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted("update:time")).toBeUndefined();
- });
-
- it("should emit input event when time inserted is in the future", async () => {
- setup({
- time: "12:30",
- allowPast: false,
- });
-
- const input = wrapper
- .findComponent({ name: "v-text-field" })
- .find("input");
-
- await input.trigger("focus");
- await input.setValue("03:11");
-
- jest.advanceTimersByTime(1000);
- expect(wrapper.emitted("update:time")).toHaveLength(1);
- });
- });
-
- describe("when time in the past is allowed", () => {
- it("should enable selection of time in the past", async () => {
- setup({ time: "12:30" });
-
- const textField = wrapper.findComponent({ name: "v-text-field" });
- const input = textField.find("input");
- expect(input.exists()).toBe(true);
- await input.trigger("click");
-
- const oneOClockListItem = wrapper
- .findAll({ name: "v-list-item" })
- .at(0); // 00:00
- expect(oneOClockListItem.attributes()["aria-disabled"]).toBeUndefined();
- });
-
- it("should emit input event when time inserted is in the past", async () => {
- setup({ time: "12:30" });
-
- const input = wrapper
- .findComponent({ name: "v-text-field" })
- .find("input");
-
- await input.trigger("focus");
- await input.setValue("02:00");
-
- jest.advanceTimersByTime(1000);
- expect(wrapper.emitted("update:time")).toHaveLength(1);
- });
- });
});
});
diff --git a/src/components/feature-date-time-picker/TimePicker.vue b/src/components/feature-date-time-picker/TimePicker.vue
index 3c86e2da27..3e5c94be77 100644
--- a/src/components/feature-date-time-picker/TimePicker.vue
+++ b/src/components/feature-date-time-picker/TimePicker.vue
@@ -37,7 +37,6 @@
:data-testid="`time-select-${index}`"
class="time-list-item text-left"
@click="onSelect(timeOfDay.value)"
- :disabled="timeOfDay.disabled"
>
{{ timeOfDay.value }}
@@ -51,7 +50,7 @@
+
+
diff --git a/src/components/ui-light-box/index.ts b/src/components/ui-light-box/index.ts
new file mode 100644
index 0000000000..f077df65fe
--- /dev/null
+++ b/src/components/ui-light-box/index.ts
@@ -0,0 +1,4 @@
+import { LightBoxOptions, useLightBox } from "./LightBox.composable";
+import LightBox from "./LightBox.vue";
+
+export { LightBox, LightBoxOptions, useLightBox };
diff --git a/src/composables/i18n.composable.ts b/src/composables/i18n.composable.ts
index ea7bb79c22..1b9c9ec12a 100644
--- a/src/composables/i18n.composable.ts
+++ b/src/composables/i18n.composable.ts
@@ -12,8 +12,11 @@ export const useI18n = () => {
return i18n.te(key, locale);
};
+ const locale = i18n.locale;
+
return {
t,
te,
+ locale,
};
};
diff --git a/src/locales/de.json b/src/locales/de.json
index 54ea984fe3..2b0ce093bb 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -137,6 +137,7 @@
"common.words.languages.en": "Englisch",
"common.words.languages.es": "Spanisch",
"common.words.languages.uk": "Ukrainisch",
+ "components.datePicker.messages.future": "Bitte Datum und Uhrzeit in der Zukunft angeben.",
"components.datePicker.validation.required": "Bitte Datum angeben.",
"components.timePicker.validation.required": "Bitte Uhrzeit angeben.",
"components.timePicker.validation.format": "Bitte Format HH:MM verwenden.",
@@ -735,8 +736,8 @@
"pages.administration.school.index.authSystems.copyLink": "Link kopieren",
"pages.administration.school.index.authSystems.edit": "{system} bearbeiten",
"pages.administration.school.index.authSystems.delete": "{system} löschen",
- "pages.administration.classes.index.title": "Klassen verwalten",
- "pages.administration.classes.index.add": "Klasse hinzufügen",
+ "pages.administration.classes.index.title": "Klassen verwalten",
+ "pages.administration.classes.index.add": "Klasse hinzufügen",
"pages.content._id.addToTopic": "Hinzufügen zu",
"pages.content._id.collection.selectElements": "Wählen Sie die Elemente, die Sie zum Thema hinzufügen möchten",
"pages.content._id.metadata.author": "Autor",
diff --git a/src/locales/en.json b/src/locales/en.json
index bf558ace10..1e6dadebce 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -136,6 +136,7 @@
"common.words.languages.en": "English",
"common.words.languages.es": "Spanish",
"common.words.languages.uk": "Ukrainian",
+ "components.datePicker.messages.future": "Please specify a date and time in the future.",
"components.datePicker.validation.required": "Please enter a date.",
"components.timePicker.validation.required": "Please enter a time.",
"components.timePicker.validation.format": "Please use format HH:MM",
@@ -733,8 +734,8 @@
"pages.administration.school.index.authSystems.copyLink": "Copy Link",
"pages.administration.school.index.authSystems.edit": "Edit {system}",
"pages.administration.school.index.authSystems.delete": "Delete {system}",
- "pages.administration.classes.index.title": "Manage classes",
- "pages.administration.classes.index.add": "Add class",
+ "pages.administration.classes.index.title": "Manage classes",
+ "pages.administration.classes.index.add": "Add class",
"pages.content._id.addToTopic": "To be added to",
"pages.content._id.collection.selectElements": "Select the items you want to add to the topic",
"pages.content._id.metadata.author": "Author",
diff --git a/src/locales/es.json b/src/locales/es.json
index 896bc1b7a9..9e199408ad 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -136,6 +136,7 @@
"common.words.languages.en": "Inglés",
"common.words.languages.es": "Español",
"common.words.languages.uk": "Ucranio",
+ "components.datePicker.messages.future": "Por favor, especifique una fecha y una hora en el futuro.",
"components.datePicker.validation.required": "Por favor ingrese una fecha.",
"components.timePicker.validation.required": "Por favor ingrese un tiempo.",
"components.timePicker.validation.format": "Por favor utilice el formato HH:MM",
@@ -722,8 +723,8 @@
"pages.administration.school.index.authSystems.copyLink": "Copiar enlace",
"pages.administration.school.index.authSystems.edit": "Editar {system}",
"pages.administration.school.index.authSystems.delete": "Eliminar {system}",
- "pages.administration.classes.index.title": "Administrar clases",
- "pages.administration.classes.index.add": "Agregar clase",
+ "pages.administration.classes.index.title": "Administrar clases",
+ "pages.administration.classes.index.add": "Agregar clase",
"pages.content._id.addToTopic": "Para ser añadido a",
"pages.content._id.collection.selectElements": "Selecciona los elementos que deses añadir al tema",
"pages.content._id.metadata.author": "Autor",
diff --git a/src/locales/uk.json b/src/locales/uk.json
index 3748f37625..0c2741cc2f 100644
--- a/src/locales/uk.json
+++ b/src/locales/uk.json
@@ -137,6 +137,7 @@
"common.words.languages.en": "Англійська",
"common.words.languages.es": "Іспанська",
"common.words.languages.uk": "Українська",
+ "components.datePicker.messages.future": "Будь ласка, вкажіть дату та час у майбутньому.",
"components.datePicker.validation.required": "Будь ласка, введіть дату.",
"components.timePicker.validation.required": "Будь ласка, введіть час.",
"components.timePicker.validation.format": "Використовуйте формат ГГ:ХХ",
@@ -812,8 +813,8 @@
"pages.administration.teachers.new.success": "Викладача успішно створено!",
"pages.administration.teachers.new.title": "Додати викладача",
"pages.administration.teachers.table.edit.ariaLabel": "Редагування вчителя",
- "pages.administration.classes.index.title": "Керувати заняттями",
- "pages.administration.classes.index.add": "Додати клас",
+ "pages.administration.classes.index.title": "Керувати заняттями",
+ "pages.administration.classes.index.add": "Додати клас",
"pages.content._id.addToTopic": "Для додавання в",
"pages.content._id.collection.selectElements": "Виберіть елементи, які треба додати до теми",
"pages.content._id.metadata.author": "Автор",
diff --git a/src/pages/rooms/tools/RoomExternalToolsSection.unit.ts b/src/pages/rooms/tools/RoomExternalToolsSection.unit.ts
index d23ae92ab2..015ec19160 100644
--- a/src/pages/rooms/tools/RoomExternalToolsSection.unit.ts
+++ b/src/pages/rooms/tools/RoomExternalToolsSection.unit.ts
@@ -1,7 +1,6 @@
import { createMock, DeepMocked } from "@golevelup/ts-jest";
import { mount, MountOptions, Wrapper } from "@vue/test-utils";
import createComponentMocks from "@@/tests/test-utils/componentMocks";
-import { AxiosError } from "axios";
import Vue from "vue";
import {
AUTH_MODULE_KEY,
@@ -13,27 +12,34 @@ import {
import {
businessErrorFactory,
externalToolDisplayDataFactory,
+ toolLaunchRequestResponseFactory,
} from "@@/tests/test-utils/factory";
import {
ExternalToolDisplayData,
+ ToolConfigurationStatus,
ToolContextType,
} from "@/store/external-tool";
import AuthModule from "@/store/auth";
import ContextExternalToolsModule from "@/store/context-external-tools";
-import ExternalToolsModule from "@/store/external-tools";
-import { BusinessError } from "@/store/types/commons";
import { createModuleMocks } from "@/utils/mock-store-module";
import VueRouter from "vue-router";
import * as routerComposables from "vue-router/composables";
-import EnvConfigModule from "../../../store/env-config";
+import EnvConfigModule from "@/store/env-config";
import RoomExternalToolsSection from "./RoomExternalToolsSection.vue";
+import ExternalToolsModule from "@/store/external-tools";
+import flushPromises from "flush-promises";
+import { BusinessError } from "@/store/types/commons";
+import { AxiosError } from "axios";
describe("RoomExternalToolsSection", () => {
let router: DeepMocked;
const getWrapper = (
- props: { tools: ExternalToolDisplayData[]; roomId: string },
- externalToolsModuleGetter?: Partial
+ props: {
+ tools: ExternalToolDisplayData[];
+ roomId: string;
+ },
+ externalToolsModuleMock = createModuleMocks(ExternalToolsModule)
) => {
document.body.setAttribute("data-app", "true");
@@ -41,15 +47,6 @@ describe("RoomExternalToolsSection", () => {
ContextExternalToolsModule
);
- const externalToolsModule = createModuleMocks(ExternalToolsModule, {
- getBusinessError: {
- statusCode: "",
- message: "",
- error: undefined,
- },
- ...externalToolsModuleGetter,
- });
-
const authModule = createModuleMocks(AuthModule, {
getUserPermissions: ["CONTEXT_TOOL_ADMIN"],
getUserRoles: ["teacher"],
@@ -77,7 +74,7 @@ describe("RoomExternalToolsSection", () => {
},
[CONTEXT_EXTERNAL_TOOLS_MODULE_KEY.valueOf()]:
contextExternalToolsModule,
- [EXTERNAL_TOOLS_MODULE_KEY.valueOf()]: externalToolsModule,
+ [EXTERNAL_TOOLS_MODULE_KEY.valueOf()]: externalToolsModuleMock,
[AUTH_MODULE_KEY.valueOf()]: authModule,
[ENV_CONFIG_MODULE_KEY.valueOf()]: envConfigModule,
},
@@ -87,7 +84,6 @@ describe("RoomExternalToolsSection", () => {
return {
wrapper,
contextExternalToolsModule,
- externalToolsModule,
authModule,
};
};
@@ -256,135 +252,132 @@ describe("RoomExternalToolsSection", () => {
});
describe("when clicking on a tool", () => {
- const setup = () => {
- const tool: ExternalToolDisplayData =
- externalToolDisplayDataFactory.build();
-
- const { wrapper, externalToolsModule } = getWrapper({
- tools: [tool],
- roomId: "roomId",
- });
-
- return {
- wrapper,
- externalToolsModule,
- tool,
+ describe("when the tool has missing auto parameters and loading requestData throw an error", () => {
+ const setup = async () => {
+ const tool: ExternalToolDisplayData =
+ externalToolDisplayDataFactory.build({
+ status: ToolConfigurationStatus.Latest,
+ });
+
+ const error: BusinessError = businessErrorFactory.build({
+ error: new AxiosError("this error is expected"),
+ message: "MISSING_TOOL_PARAMETER_VALUE some value is missing",
+ });
+
+ const externalToolsModule = createModuleMocks(ExternalToolsModule);
+ externalToolsModule.loadToolLaunchData.mockRejectedValueOnce(error);
+
+ const { wrapper } = getWrapper(
+ { tools: [tool], roomId: "roomId" },
+ externalToolsModule
+ );
+
+ await flushPromises();
+
+ return {
+ wrapper,
+ tool,
+ };
};
- };
- it("should fetch the launch data", async () => {
- const { wrapper, externalToolsModule, tool } = setup();
+ it("should open up the error dialog", async () => {
+ const { wrapper } = await setup();
- const card = wrapper.findComponent({
- name: "room-external-tool-card",
- });
+ const card = wrapper.findComponent({
+ name: "room-external-tool-card",
+ });
- await card.trigger("click");
+ await card.trigger("click");
- expect(externalToolsModule.loadToolLaunchData).toHaveBeenCalledWith(
- tool.contextExternalToolId
- );
+ const dialog = wrapper.find('[data-testId="error-dialog"]');
+ expect(dialog.exists()).toBeTruthy();
+ expect(wrapper.vm.isErrorDialogOpen).toBeTruthy();
+ });
});
- });
-
- describe("when clicking on a tool which has missing auto parameters", () => {
- const setup = () => {
- const tool: ExternalToolDisplayData =
- externalToolDisplayDataFactory.build();
- const error: BusinessError = businessErrorFactory.build({
- error: new AxiosError("this error is expected"),
- message: "MISSING_TOOL_PARAMETER_VALUE some value is missing",
- });
+ describe("when the tool is launchable", () => {
+ const setup = async () => {
+ const tool: ExternalToolDisplayData =
+ externalToolDisplayDataFactory.build({
+ status: ToolConfigurationStatus.Latest,
+ });
+
+ const externalToolsModule = createModuleMocks(ExternalToolsModule);
+ externalToolsModule.loadToolLaunchData.mockResolvedValue(
+ toolLaunchRequestResponseFactory.build()
+ );
+
+ const { wrapper } = getWrapper(
+ {
+ tools: [tool],
+ roomId: "roomId",
+ },
+ externalToolsModule
+ );
- const { wrapper } = getWrapper(
- { tools: [tool], roomId: "roomId" },
- {
- getBusinessError: error,
- }
- );
+ await flushPromises();
- return {
- wrapper,
- tool,
+ return {
+ wrapper,
+ };
};
- };
- it("should display a dialog", async () => {
- const { wrapper, tool } = setup();
+ it("should not open up the error dialog", async () => {
+ const { wrapper } = await setup();
- const card = wrapper.findComponent({
- name: "room-external-tool-card",
- });
- await card.vm.$emit("click", tool);
+ const card = wrapper.findComponent({
+ name: "room-external-tool-card",
+ });
- const dialog = wrapper.find('[data-testId="error-dialog"]');
+ await card.trigger("click");
- expect(dialog.exists()).toBeTruthy();
- expect(wrapper.vm.isErrorDialogOpen).toBeTruthy();
- });
- });
-
- describe("when click on a outdated tool", () => {
- const setup = () => {
- const tool: ExternalToolDisplayData =
- externalToolDisplayDataFactory.build();
+ wrapper.find('[data-testId="error-dialog"]');
- const error: BusinessError = businessErrorFactory.build({
- error: new AxiosError("this error is expected"),
- message: "TOOL_STATUS_OUTDATED this tool is outdated",
+ expect(wrapper.vm.isErrorDialogOpen).toBeFalsy();
});
+ });
- const { wrapper } = getWrapper(
- { tools: [tool], roomId: "roomId" },
- {
- getBusinessError: error,
- }
- );
-
- return {
- wrapper,
- tool,
- };
- };
-
- it("should display a dialog", async () => {
- const { wrapper, tool } = setup();
-
- const card = wrapper.findComponent({
- name: "room-external-tool-card",
- });
- await card.vm.$emit("click", tool);
+ describe("and tool is not launchable because it is outdated", () => {
+ const setup = async () => {
+ const tool: ExternalToolDisplayData =
+ externalToolDisplayDataFactory.build({
+ status: ToolConfigurationStatus.Outdated,
+ });
+
+ const externalToolsModule = createModuleMocks(ExternalToolsModule);
+ externalToolsModule.loadToolLaunchData.mockResolvedValueOnce(
+ toolLaunchRequestResponseFactory.build()
+ );
+
+ const { wrapper } = getWrapper(
+ {
+ tools: [tool],
+ roomId: "roomId",
+ },
+ externalToolsModule
+ );
- const dialog = wrapper.find('[data-testId="error-dialog"]');
+ await flushPromises();
- expect(dialog.exists()).toBeTruthy();
- expect(wrapper.vm.isErrorDialogOpen).toBeTruthy();
- });
- });
+ return {
+ wrapper,
+ };
+ };
- describe("when click on a latest tool", () => {
- const setup = () => {
- const tool: ExternalToolDisplayData =
- externalToolDisplayDataFactory.build();
+ it("should open up the error dialog", async () => {
+ const { wrapper } = await setup();
- const { wrapper } = getWrapper({ tools: [tool], roomId: "roomId" });
+ const card = wrapper.findComponent({
+ name: "room-external-tool-card",
+ });
- return {
- wrapper,
- tool,
- };
- };
+ await card.trigger("click");
- it("should not display a dialog", async () => {
- const { wrapper, tool } = setup();
+ const dialog = wrapper.find('[data-testId="error-dialog"]');
- const card = wrapper.findComponent({
- name: "room-external-tool-card",
+ expect(dialog.exists()).toBeTruthy();
+ expect(wrapper.vm.isErrorDialogOpen).toBeTruthy();
});
- await card.vm.$emit("click", tool);
-
- expect(wrapper.vm.isErrorDialogOpen).toBeFalsy();
});
});
});
diff --git a/src/pages/rooms/tools/RoomExternalToolsSection.vue b/src/pages/rooms/tools/RoomExternalToolsSection.vue
index a8cb87f66b..b643685cd0 100644
--- a/src/pages/rooms/tools/RoomExternalToolsSection.vue
+++ b/src/pages/rooms/tools/RoomExternalToolsSection.vue
@@ -10,6 +10,7 @@
@delete="onOpenDeleteDialog"
@edit="onEditTool"
@click="onClickTool"
+ @error="onError"
:data-testid="`external-tool-card-${index}`"
>
@@ -85,19 +86,12 @@