From 6e4ef53b2953f66b9b48accb737b8313058feae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= <103562092+MarvinOehlerkingCap@users.noreply.github.com> Date: Fri, 3 Nov 2023 07:34:19 +0100 Subject: [PATCH 1/5] N21-1397 Generate API (#2885) --- src/serverApi/v3/api.ts | 26 +++++++++++++------------- src/store/group.ts | 2 +- src/store/group.unit.ts | 10 ++++------ 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/serverApi/v3/api.ts b/src/serverApi/v3/api.ts index c11606e3c8..87daf55d22 100644 --- a/src/serverApi/v3/api.ts +++ b/src/serverApi/v3/api.ts @@ -8953,7 +8953,7 @@ export const GroupApiAxiosParamCreator = function (configuration?: Configuration return { /** * - * @summary Get a list of classes and groups of type class for the current users school. + * @summary Get a list of classes and groups of type class for the current user. * @param {number} [skip] Number of elements (not pages) to be skipped * @param {number} [limit] Page limit, defaults to 10. * @param {'asc' | 'desc'} [sortOrder] @@ -8961,7 +8961,7 @@ export const GroupApiAxiosParamCreator = function (configuration?: Configuration * @param {*} [options] Override http request option. * @throws {RequiredError} */ - groupControllerFindClassesForSchool: async (skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options: any = {}): Promise => { + groupControllerFindClasses: async (skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options: any = {}): Promise => { const localVarPath = `/groups/class`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -9055,7 +9055,7 @@ export const GroupApiFp = function(configuration?: Configuration) { return { /** * - * @summary Get a list of classes and groups of type class for the current users school. + * @summary Get a list of classes and groups of type class for the current user. * @param {number} [skip] Number of elements (not pages) to be skipped * @param {number} [limit] Page limit, defaults to 10. * @param {'asc' | 'desc'} [sortOrder] @@ -9063,8 +9063,8 @@ export const GroupApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async groupControllerFindClassesForSchool(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.groupControllerFindClassesForSchool(skip, limit, sortOrder, sortBy, options); + async groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.groupControllerFindClasses(skip, limit, sortOrder, sortBy, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -9090,7 +9090,7 @@ export const GroupApiFactory = function (configuration?: Configuration, basePath return { /** * - * @summary Get a list of classes and groups of type class for the current users school. + * @summary Get a list of classes and groups of type class for the current user. * @param {number} [skip] Number of elements (not pages) to be skipped * @param {number} [limit] Page limit, defaults to 10. * @param {'asc' | 'desc'} [sortOrder] @@ -9098,8 +9098,8 @@ export const GroupApiFactory = function (configuration?: Configuration, basePath * @param {*} [options] Override http request option. * @throws {RequiredError} */ - groupControllerFindClassesForSchool(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options?: any): AxiosPromise { - return localVarFp.groupControllerFindClassesForSchool(skip, limit, sortOrder, sortBy, options).then((request) => request(axios, basePath)); + groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options?: any): AxiosPromise { + return localVarFp.groupControllerFindClasses(skip, limit, sortOrder, sortBy, options).then((request) => request(axios, basePath)); }, /** * @@ -9122,7 +9122,7 @@ export const GroupApiFactory = function (configuration?: Configuration, basePath export interface GroupApiInterface { /** * - * @summary Get a list of classes and groups of type class for the current users school. + * @summary Get a list of classes and groups of type class for the current user. * @param {number} [skip] Number of elements (not pages) to be skipped * @param {number} [limit] Page limit, defaults to 10. * @param {'asc' | 'desc'} [sortOrder] @@ -9131,7 +9131,7 @@ export interface GroupApiInterface { * @throws {RequiredError} * @memberof GroupApiInterface */ - groupControllerFindClassesForSchool(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options?: any): AxiosPromise; + groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options?: any): AxiosPromise; /** * @@ -9154,7 +9154,7 @@ export interface GroupApiInterface { export class GroupApi extends BaseAPI implements GroupApiInterface { /** * - * @summary Get a list of classes and groups of type class for the current users school. + * @summary Get a list of classes and groups of type class for the current user. * @param {number} [skip] Number of elements (not pages) to be skipped * @param {number} [limit] Page limit, defaults to 10. * @param {'asc' | 'desc'} [sortOrder] @@ -9163,8 +9163,8 @@ export class GroupApi extends BaseAPI implements GroupApiInterface { * @throws {RequiredError} * @memberof GroupApi */ - public groupControllerFindClassesForSchool(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options?: any) { - return GroupApiFp(this.configuration).groupControllerFindClassesForSchool(skip, limit, sortOrder, sortBy, options).then((request) => request(this.axios, this.basePath)); + public groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'name' | 'externalSourceName', options?: any) { + return GroupApiFp(this.configuration).groupControllerFindClasses(skip, limit, sortOrder, sortBy, options).then((request) => request(this.axios, this.basePath)); } /** diff --git a/src/store/group.ts b/src/store/group.ts index d27dc28750..d3c1e49f91 100644 --- a/src/store/group.ts +++ b/src/store/group.ts @@ -137,7 +137,7 @@ export default class GroupModule extends VuexModule { : undefined; const response: AxiosResponse = - await this.groupApi.groupControllerFindClassesForSchool( + await this.groupApi.groupControllerFindClasses( this.pagination.skip, this.pagination.limit, this.getSortOrder, diff --git a/src/store/group.unit.ts b/src/store/group.unit.ts index 3c92c477b0..64263609e2 100644 --- a/src/store/group.unit.ts +++ b/src/store/group.unit.ts @@ -199,7 +199,7 @@ describe("GroupModule", () => { limit: pagination.limit, }); - apiMock.groupControllerFindClassesForSchool.mockResolvedValue( + apiMock.groupControllerFindClasses.mockResolvedValue( mockApiResponse({ data: response }) ); @@ -217,9 +217,7 @@ describe("GroupModule", () => { await module.loadClassesForSchool(); - expect( - apiMock.groupControllerFindClassesForSchool - ).toHaveBeenCalledWith( + expect(apiMock.groupControllerFindClasses).toHaveBeenCalledWith( pagination.skip, pagination.limit, sortOrder, @@ -242,7 +240,7 @@ describe("GroupModule", () => { const error = axiosErrorFactory.build(); const apiError = mapAxiosErrorToResponseError(error); - apiMock.groupControllerFindClassesForSchool.mockRejectedValue(error); + apiMock.groupControllerFindClasses.mockRejectedValue(error); return { apiError, @@ -288,7 +286,7 @@ describe("GroupModule", () => { await module.deleteClass(class1.id); - expect(apiMock.groupControllerFindClassesForSchool).toHaveBeenCalled(); + expect(apiMock.groupControllerFindClasses).toHaveBeenCalled(); }); }); From 7992b2f0f6dd502e818269ca49e7ade6410ac810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= <103562092+MarvinOehlerkingCap@users.noreply.github.com> Date: Fri, 3 Nov 2023 08:44:58 +0100 Subject: [PATCH 2/5] N21-1285 User launches a CTL tool from a board card (#2891) - create composable for tool launching - add tool launching to external tool element - delete old external tool store --- .../ExternalToolApi.composable.ts | 32 +++ .../ExternalToolApi.composable.unit.ts | 65 +++++ ...ToolElementDisplayState.composable.unit.ts | 4 +- .../ExternalToolLaunchState.composable.ts | 101 +++++++ ...ExternalToolLaunchState.composable.unit.ts | 270 ++++++++++++++++++ src/components/data-external-tool/index.ts | 1 + .../ExternalToolConfigSettings.unit.ts | 51 +--- .../ExternalToolConfigSettings.vue | 21 +- .../ExternalToolElement.unit.ts | 133 ++++++++- .../ExternalToolElement.vue | 35 ++- .../rooms/RoomExternalToolCard.unit.ts | 143 ++++++---- src/components/rooms/RoomExternalToolCard.vue | 73 ++--- src/main.ts | 41 ++- .../tools/RoomExternalToolsSection.unit.ts | 160 ++--------- .../rooms/tools/RoomExternalToolsSection.vue | 68 ----- src/store/external-tools.ts | 54 ---- src/store/external-tools.unit.ts | 94 ------ src/store/store-accessor.ts | 14 +- src/utils/inject/injection-keys.ts | 25 +- .../factory/toolLaunchRequestFactory.ts | 4 +- 20 files changed, 789 insertions(+), 600 deletions(-) create mode 100644 src/components/data-external-tool/ExternalToolApi.composable.ts create mode 100644 src/components/data-external-tool/ExternalToolApi.composable.unit.ts create mode 100644 src/components/data-external-tool/ExternalToolLaunchState.composable.ts create mode 100644 src/components/data-external-tool/ExternalToolLaunchState.composable.unit.ts delete mode 100644 src/store/external-tools.ts delete mode 100644 src/store/external-tools.unit.ts diff --git a/src/components/data-external-tool/ExternalToolApi.composable.ts b/src/components/data-external-tool/ExternalToolApi.composable.ts new file mode 100644 index 0000000000..9848ede046 --- /dev/null +++ b/src/components/data-external-tool/ExternalToolApi.composable.ts @@ -0,0 +1,32 @@ +import { + ToolApiFactory, + ToolApiInterface, + ToolLaunchRequestResponse, +} from "@/serverApi/v3"; +import { ToolLaunchRequest } from "@/store/external-tool"; +import { ExternalToolMapper } from "@/store/external-tool/mapper"; +import { $axios } from "@/utils/api"; +import { AxiosResponse } from "axios"; + +export const useExternalToolApi = () => { + const toolApi: ToolApiInterface = ToolApiFactory(undefined, "/v3", $axios); + + const fetchLaunchDataCall = async ( + contextExternalToolId: string + ): Promise => { + const response: AxiosResponse = + await toolApi.toolLaunchControllerGetToolLaunchRequest( + contextExternalToolId + ); + + const mapped: ToolLaunchRequest = ExternalToolMapper.mapToToolLaunchRequest( + response.data + ); + + return mapped; + }; + + return { + fetchLaunchDataCall, + }; +}; diff --git a/src/components/data-external-tool/ExternalToolApi.composable.unit.ts b/src/components/data-external-tool/ExternalToolApi.composable.unit.ts new file mode 100644 index 0000000000..b025e3da78 --- /dev/null +++ b/src/components/data-external-tool/ExternalToolApi.composable.unit.ts @@ -0,0 +1,65 @@ +import * as serverApi from "@/serverApi/v3/api"; +import { ToolLaunchRequestResponse } from "@/serverApi/v3/api"; +import { + ToolLaunchRequest, + ToolLaunchRequestMethodEnum, +} from "@/store/external-tool"; +import { + mockApiResponse, + toolLaunchRequestResponseFactory, +} from "@@/tests/test-utils"; +import { createMock, DeepMocked } from "@golevelup/ts-jest"; +import { useExternalToolApi } from "./ExternalToolApi.composable"; + +describe("ExternalToolApi.composable", () => { + let toolApi: DeepMocked; + + beforeEach(() => { + toolApi = createMock(); + + jest.spyOn(serverApi, "ToolApiFactory").mockReturnValue(toolApi); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("fetchLaunchDataCall", () => { + const setup = () => { + const launchRequest: ToolLaunchRequestResponse = + toolLaunchRequestResponseFactory.build(); + + toolApi.toolLaunchControllerGetToolLaunchRequest.mockResolvedValue( + mockApiResponse({ data: launchRequest }) + ); + + return { + launchRequest, + }; + }; + + it("should call the api for a tool launch request", async () => { + setup(); + + await useExternalToolApi().fetchLaunchDataCall("contextExternalToolId"); + + expect( + toolApi.toolLaunchControllerGetToolLaunchRequest + ).toHaveBeenCalledWith("contextExternalToolId"); + }); + + it("should return launch request data", async () => { + const { launchRequest } = setup(); + + const result: ToolLaunchRequest = + await useExternalToolApi().fetchLaunchDataCall("contextExternalToolId"); + + expect(result).toEqual({ + url: launchRequest.url, + payload: launchRequest.payload, + method: ToolLaunchRequestMethodEnum.Get, + openNewTab: launchRequest.openNewTab, + }); + }); + }); +}); diff --git a/src/components/data-external-tool/ExternalToolElementDisplayState.composable.unit.ts b/src/components/data-external-tool/ExternalToolElementDisplayState.composable.unit.ts index 1fe5ff37ed..1a9b629b0d 100644 --- a/src/components/data-external-tool/ExternalToolElementDisplayState.composable.unit.ts +++ b/src/components/data-external-tool/ExternalToolElementDisplayState.composable.unit.ts @@ -2,10 +2,10 @@ import { ExternalToolDisplayData } from "@/store/external-tool"; import { externalToolDisplayDataFactory } from "@@/tests/test-utils"; import { createMock, DeepMocked } from "@golevelup/ts-jest"; import { useErrorHandler } from "../error-handling/ErrorHandler.composable"; +import { useContextExternalToolApi } from "./ContextExternalToolApi.composable"; import { useExternalToolElementDisplayState } from "./ExternalToolElementDisplayState.composable"; -import { useContextExternalToolApi } from "./index"; -jest.mock("@data-external-tool"); +jest.mock("@data-external-tool/ContextExternalToolApi.composable"); jest.mock("@/components/error-handling/ErrorHandler.composable"); describe("ExternalToolElementDisplayState.composable", () => { diff --git a/src/components/data-external-tool/ExternalToolLaunchState.composable.ts b/src/components/data-external-tool/ExternalToolLaunchState.composable.ts new file mode 100644 index 0000000000..d4b8b61673 --- /dev/null +++ b/src/components/data-external-tool/ExternalToolLaunchState.composable.ts @@ -0,0 +1,101 @@ +import { + ToolLaunchRequest, + ToolLaunchRequestMethodEnum, +} from "@/store/external-tool"; +import { BusinessError } from "@/store/types/commons"; +import { HttpStatusCode } from "@/store/types/http-status-code.enum"; +import { mapAxiosErrorToResponseError } from "@/utils/api"; +import { ref, Ref } from "vue"; +import { useExternalToolApi } from "./ExternalToolApi.composable"; + +export const useExternalToolLaunchState = () => { + const { fetchLaunchDataCall } = useExternalToolApi(); + + const isLoading: Ref = ref(false); + const error: Ref = ref(); + const toolLaunchRequest: Ref = ref(); + + const fetchLaunchRequest = async ( + contextExternalToolId: string + ): Promise => { + isLoading.value = true; + error.value = undefined; + + try { + toolLaunchRequest.value = await fetchLaunchDataCall( + contextExternalToolId + ); + } catch (axiosError: unknown) { + const apiError = mapAxiosErrorToResponseError(axiosError); + + error.value = { + error: apiError, + message: apiError.message, + statusCode: apiError.code, + }; + } + + isLoading.value = false; + }; + + const launchTool = () => { + if (!toolLaunchRequest.value) { + return; + } + + switch (toolLaunchRequest.value.method) { + case ToolLaunchRequestMethodEnum.Get: + handleGetLaunchRequest(toolLaunchRequest.value); + break; + case ToolLaunchRequestMethodEnum.Post: + handlePostLaunchRequest(toolLaunchRequest.value); + break; + default: + error.value = { + message: "Unknown launch method", + statusCode: HttpStatusCode.UnprocessableEntity, + }; + break; + } + }; + + const handleGetLaunchRequest = (toolLaunch: ToolLaunchRequest) => { + if (toolLaunch.openNewTab) { + window.open(toolLaunch.url, "_blank"); + return; + } + window.location.href = toolLaunch.url; + }; + + const handlePostLaunchRequest = (toolLaunch: ToolLaunchRequest) => { + const form: HTMLFormElement = document.createElement("form"); + form.method = "POST"; + form.action = toolLaunch.url; + form.target = toolLaunch.openNewTab ? "_blank" : "_self"; + form.id = "launch-form"; + + const payload = JSON.parse(toolLaunch.payload || "{}"); + + for (const key in payload) { + if (Object.prototype.hasOwnProperty.call(payload, key)) { + const hiddenField = document.createElement("input"); + hiddenField.type = "hidden"; + hiddenField.name = key; + hiddenField.value = payload[key]; + + form.appendChild(hiddenField); + } + } + + document.body.appendChild(form); + form.submit(); + }; + + return { + toolLaunchRequest, + error, + isLoading, + fetchLaunchRequest, + launchTool, + }; +}; diff --git a/src/components/data-external-tool/ExternalToolLaunchState.composable.unit.ts b/src/components/data-external-tool/ExternalToolLaunchState.composable.unit.ts new file mode 100644 index 0000000000..053a45bb3d --- /dev/null +++ b/src/components/data-external-tool/ExternalToolLaunchState.composable.unit.ts @@ -0,0 +1,270 @@ +import { + ToolLaunchRequest, + ToolLaunchRequestMethodEnum, +} from "@/store/external-tool"; +import { BusinessError } from "@/store/types/commons"; +import { HttpStatusCode } from "@/store/types/http-status-code.enum"; +import { toolLaunchRequestFactory } from "@@/tests/test-utils/factory/toolLaunchRequestFactory"; +import { createMock, DeepMocked } from "@golevelup/ts-jest"; +import { axiosErrorFactory } from "../../../tests/test-utils"; +import { mapAxiosErrorToResponseError } from "../../utils/api"; +import { useExternalToolApi } from "./ExternalToolApi.composable"; +import { useExternalToolLaunchState } from "./ExternalToolLaunchState.composable"; + +jest.mock("@data-external-tool/ExternalToolApi.composable"); + +describe("ExternalToolLaunchState.composable", () => { + let useExternalToolApiMock: DeepMocked>; + + beforeEach(() => { + useExternalToolApiMock = + createMock>(); + + jest.mocked(useExternalToolApi).mockReturnValue(useExternalToolApiMock); + }); + + afterEach(() => { + jest.clearAllMocks(); + + window.location.href = ""; + }); + + describe("fetchLaunchRequest", () => { + describe("when fetching a tool", () => { + const setup = () => { + const response = toolLaunchRequestFactory.build(); + + useExternalToolApiMock.fetchLaunchDataCall.mockResolvedValue(response); + + return { + ...useExternalToolLaunchState(), + response, + }; + }; + + it("should load the launch data from the store", async () => { + const { fetchLaunchRequest } = setup(); + + await fetchLaunchRequest("contextExternalToolId"); + + expect(useExternalToolApiMock.fetchLaunchDataCall).toHaveBeenCalledWith( + "contextExternalToolId" + ); + }); + + it("should save the loaded request in a state", async () => { + const { fetchLaunchRequest, toolLaunchRequest, response } = setup(); + + await fetchLaunchRequest("contextExternalToolId"); + + expect(toolLaunchRequest.value).toEqual({ + method: ToolLaunchRequestMethodEnum.Get, + url: response.url, + payload: response.payload, + openNewTab: response.openNewTab, + }); + }); + + it("should not have an error", async () => { + const { fetchLaunchRequest, error } = setup(); + + await fetchLaunchRequest("contextExternalToolId"); + + expect(error.value).toBeUndefined(); + }); + }); + + describe("when an error occurs", () => { + const setup = () => { + const axiosError = axiosErrorFactory.build(); + const apiError = mapAxiosErrorToResponseError(axiosError); + + useExternalToolApiMock.fetchLaunchDataCall.mockRejectedValue( + axiosError + ); + + return { + ...useExternalToolLaunchState(), + apiError, + }; + }; + + it("should load the launch data from the store", async () => { + const { fetchLaunchRequest, error, apiError } = setup(); + + await fetchLaunchRequest("contextExternalToolId"); + + expect(error.value).toEqual({ + message: apiError.message, + statusCode: apiError.code, + error: apiError, + }); + }); + }); + }); + + describe("launchTool", () => { + describe("when launching without loading", () => { + it("should do nothing", () => { + const { launchTool } = useExternalToolLaunchState(); + + launchTool(); + + expect(window.location.href).toEqual(""); + }); + }); + + describe("when launching a tool with get method", () => { + describe("when opening in the same tab", () => { + const setup = () => { + const launchRequest = toolLaunchRequestFactory.build({ + method: ToolLaunchRequestMethodEnum.Get, + openNewTab: false, + }); + + const composable = useExternalToolLaunchState(); + composable.toolLaunchRequest.value = launchRequest; + + return { + ...composable, + launchRequest, + }; + }; + + it("should set the location", () => { + const { launchRequest, launchTool } = setup(); + + launchTool(); + + expect(window.location.href).toEqual(launchRequest.url); + }); + }); + + describe("when opening in a new tab", () => { + const setup = () => { + jest.spyOn(window, "open"); + const launchRequest = toolLaunchRequestFactory.build({ + method: ToolLaunchRequestMethodEnum.Get, + openNewTab: true, + }); + + const composable = useExternalToolLaunchState(); + composable.toolLaunchRequest.value = launchRequest; + + return { + ...composable, + launchRequest, + }; + }; + + it("should open in a new tab", () => { + const { launchRequest, launchTool } = setup(); + + launchTool(); + + expect(window.open).toHaveBeenCalledWith(launchRequest.url, "_blank"); + }); + }); + }); + + describe("when launching a tool with post method", () => { + afterEach(() => { + document.getElementById("launch-form")?.remove(); + }); + + describe("when opening in the same tab", () => { + const setup = () => { + const launchRequest = toolLaunchRequestFactory.build({ + method: ToolLaunchRequestMethodEnum.Post, + openNewTab: false, + }); + + const composable = useExternalToolLaunchState(); + composable.toolLaunchRequest.value = launchRequest; + + return { + ...composable, + launchRequest, + }; + }; + + it("should create a launch form with target _self", () => { + const { launchRequest, launchTool } = setup(); + + launchTool(); + + const form = document.getElementById("launch-form"); + + expect(form?.outerHTML).toEqual( + `
` + ); + }); + }); + + describe("when opening in a new tab", () => { + const setup = () => { + const launchRequest = toolLaunchRequestFactory.build({ + method: ToolLaunchRequestMethodEnum.Post, + openNewTab: true, + payload: "", + }); + + const composable = useExternalToolLaunchState(); + composable.toolLaunchRequest.value = launchRequest; + + return { + ...composable, + launchRequest, + }; + }; + + it("should create a launch form with target _blank", () => { + const { launchRequest, launchTool } = setup(); + + launchTool(); + + const form = document.getElementById("launch-form"); + + expect(form?.outerHTML).toEqual( + `
` + ); + }); + }); + }); + + describe("when the launch method is unknown", () => { + const setup = () => { + const launchRequest = toolLaunchRequestFactory.build({ + method: "unknown" as unknown as ToolLaunchRequestMethodEnum, + }); + + const composable = useExternalToolLaunchState(); + composable.toolLaunchRequest.value = launchRequest; + + return { + ...composable, + launchRequest, + }; + }; + + it("should set an error", () => { + const { error, launchTool } = setup(); + + launchTool(); + + expect(error.value).toEqual({ + message: "Unknown launch method", + statusCode: HttpStatusCode.UnprocessableEntity, + }); + }); + + it("should not redirect", () => { + const { launchTool } = setup(); + + launchTool(); + + expect(window.location.href).toEqual(""); + }); + }); + }); +}); diff --git a/src/components/data-external-tool/index.ts b/src/components/data-external-tool/index.ts index f64b0c1f8f..9c9d3f1c8e 100644 --- a/src/components/data-external-tool/index.ts +++ b/src/components/data-external-tool/index.ts @@ -1,2 +1,3 @@ export * from "./ContextExternalToolApi.composable"; export * from "./ExternalToolElementDisplayState.composable"; +export * from "./ExternalToolLaunchState.composable"; diff --git a/src/components/external-tools/configuration/ExternalToolConfigSettings.unit.ts b/src/components/external-tools/configuration/ExternalToolConfigSettings.unit.ts index f62f4c04b1..189942b31a 100644 --- a/src/components/external-tools/configuration/ExternalToolConfigSettings.unit.ts +++ b/src/components/external-tools/configuration/ExternalToolConfigSettings.unit.ts @@ -1,9 +1,5 @@ import { ExternalToolConfigurationTemplate } from "@/store/external-tool"; -import ExternalToolsModule from "@/store/external-tools"; -import { EXTERNAL_TOOLS_MODULE_KEY, I18N_KEY } from "@/utils/inject"; -import { createModuleMocks } from "@/utils/mock-store-module"; import { - i18nMock, schoolExternalToolConfigurationTemplateFactory, toolParameterFactory, } from "@@/tests/test-utils"; @@ -20,26 +16,16 @@ describe("ExternalToolConfigSettings", () => { } = { template: schoolExternalToolConfigurationTemplateFactory.build(), value: [], - }, - getter: Partial = {} + } ) => { document.body.setAttribute("data-app", "true"); - const externalToolsModule = createModuleMocks(ExternalToolsModule, { - getLoading: false, - ...getter, - }) as jest.Mocked; - const wrapper: Wrapper = shallowMount( ExternalToolConfigSettings as MountOptions, { ...createComponentMocks({ i18n: true, }), - provide: { - [I18N_KEY.valueOf()]: i18nMock, - [EXTERNAL_TOOLS_MODULE_KEY.valueOf()]: externalToolsModule, - }, propsData: { ...props, }, @@ -48,7 +34,6 @@ describe("ExternalToolConfigSettings", () => { return { wrapper, - externalToolsModule, }; }; @@ -62,40 +47,6 @@ describe("ExternalToolConfigSettings", () => { }); }); - describe("progressbar", () => { - it("should display progressbar when loading in store is set", () => { - const { wrapper } = getWrapper( - { - template: schoolExternalToolConfigurationTemplateFactory.build(), - value: [], - }, - { - getLoading: true, - } - ); - - const progressbar = wrapper.find("v-progress-linear-stub"); - - expect(progressbar.attributes().active).toBeTruthy(); - }); - - it("should not display progressbar when loading in store is not set", () => { - const { wrapper } = getWrapper( - { - template: schoolExternalToolConfigurationTemplateFactory.build(), - value: [], - }, - { - getLoading: false, - } - ); - - const progressbar = wrapper.find("v-progress-linear-stub"); - - expect(progressbar.attributes().active).toBeFalsy(); - }); - }); - describe("parameters", () => { const setup = () => { const template = schoolExternalToolConfigurationTemplateFactory.build({ diff --git a/src/components/external-tools/configuration/ExternalToolConfigSettings.vue b/src/components/external-tools/configuration/ExternalToolConfigSettings.vue index 816068dcce..61c023a6f0 100644 --- a/src/components/external-tools/configuration/ExternalToolConfigSettings.vue +++ b/src/components/external-tools/configuration/ExternalToolConfigSettings.vue @@ -6,24 +6,14 @@ v-model="inputValues[index]" /> -