From 6ec72585b92b37a339ac888d06083844ac7254a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= Date: Wed, 18 Sep 2024 15:52:55 +0200 Subject: [PATCH 1/3] add aria label to external tool element --- src/locales/de.ts | 1 + src/locales/en.ts | 1 + src/locales/es.ts | 1 + src/locales/uk.ts | 1 + .../ExternalToolElement.unit.ts | 231 ++++++++---- .../ExternalToolElement.vue | 333 +++++++++--------- .../externalToolElementResponseFactory.ts | 16 + tests/test-utils/factory/index.ts | 1 + 8 files changed, 344 insertions(+), 241 deletions(-) create mode 100644 tests/test-utils/factory/externalToolElementResponseFactory.ts diff --git a/src/locales/de.ts b/src/locales/de.ts index 3efc985edd..7ad1e5987a 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -25,6 +25,7 @@ export default { "common.actions.shareCopy": "Kopie teilen", "common.actions.update": "Aktualisieren", "common.ariaLabel.newTab": "öffnet in einem neuen Tab", + "common.ariaLabel.sameTab": "öffnet im selben Tab", "common.labels.admin": "Admin(s)", "common.labels.birthdate": "Geburtsdatum", "common.labels.birthday": "Geburtsdatum", diff --git a/src/locales/en.ts b/src/locales/en.ts index 49ce83b3c6..219d3b3c68 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -25,6 +25,7 @@ export default { "common.actions.shareCopy": "Share copy", "common.actions.update": "Update", "common.ariaLabel.newTab": "opens in a new tab", + "common.ariaLabel.sameTab": "opens in the same tab", "common.labels.admin": "", "common.labels.birthdate": "Date of birth", "common.labels.birthday": "Date of Birth", diff --git a/src/locales/es.ts b/src/locales/es.ts index 577a8f3423..9542bf9b30 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -25,6 +25,7 @@ export default { "common.actions.shareCopy": "Compartir copia", "common.actions.update": "Actualizar", "common.ariaLabel.newTab": "se abre en una nueva pestaña", + "common.ariaLabel.sameTab": "se abre en la misma pestaña", "common.labels.admin": "Admin(s)", "common.labels.birthdate": "Fecha de nacimiento", "common.labels.birthday": "Fecha de nacimiento", diff --git a/src/locales/uk.ts b/src/locales/uk.ts index 677427a7af..5acdea7744 100644 --- a/src/locales/uk.ts +++ b/src/locales/uk.ts @@ -25,6 +25,7 @@ export default { "common.actions.shareCopy": "Поділитися копією", "common.actions.update": "Оновити", "common.ariaLabel.newTab": "відкривається в новій вкладці", + "common.ariaLabel.sameTab": "відкривається в тій же вкладці", "common.labels.admin": "адміністратор(и)", "common.labels.birthdate": "Дата народження", "common.labels.birthday": "Дата народження", diff --git a/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts b/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts index 147e2d4be3..c075df3e9a 100644 --- a/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts +++ b/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts @@ -1,8 +1,4 @@ -import { - ConfigResponse, - ContentElementType, - ExternalToolElementResponse, -} from "@/serverApi/v3"; +import { ConfigResponse, ExternalToolElementResponse } from "@/serverApi/v3"; import EnvConfigModule from "@/store/env-config"; import { BusinessError } from "@/store/types/commons"; import { ENV_CONFIG_MODULE_KEY } from "@/utils/inject"; @@ -11,8 +7,8 @@ import { contextExternalToolConfigurationStatusFactory, contextExternalToolFactory, externalToolDisplayDataFactory, + externalToolElementResponseFactory, schoolToolConfigurationStatusFactory, - timestampsResponseFactory, } from "@@/tests/test-utils"; import { createTestingI18n, @@ -39,15 +35,6 @@ jest.mock("@data-board"); jest.mock("@data-external-tool"); jest.mock("@util-board"); -const EMPTY_TEST_ELEMENT: ExternalToolElementResponse = { - id: "external-tool-element-id", - content: { - contextExternalToolId: null, - }, - type: ContentElementType.ExternalTool, - timestamps: timestampsResponseFactory.build(), -}; - describe("ExternalToolElement", () => { let useContentElementStateMock: DeepMocked< ReturnType @@ -154,10 +141,9 @@ describe("ExternalToolElement", () => { it("should load the display data", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -175,10 +161,9 @@ describe("ExternalToolElement", () => { it("should load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -198,10 +183,9 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -224,10 +208,9 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -249,10 +232,9 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -274,10 +256,9 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -297,11 +278,11 @@ describe("ExternalToolElement", () => { describe("when the element does not have a tool attached", () => { it("should open the configuration dialog immediately", async () => { - useSharedLastCreatedElementMock.lastCreatedElementId.value = - EMPTY_TEST_ELEMENT.id; + const element = externalToolElementResponseFactory.build(); + useSharedLastCreatedElementMock.lastCreatedElementId.value = element.id; const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element, isEditMode: true, }); @@ -316,7 +297,7 @@ describe("ExternalToolElement", () => { it("should not load the display data", async () => { getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: false, }); @@ -329,7 +310,7 @@ describe("ExternalToolElement", () => { it("should not load the launch request", async () => { getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: false, }); @@ -346,7 +327,7 @@ describe("ExternalToolElement", () => { describe("when not in edit mode", () => { const setup = () => { const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: false, }); @@ -367,7 +348,7 @@ describe("ExternalToolElement", () => { describe("when in edit mode", () => { const setup = () => { const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }); @@ -393,10 +374,9 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, - content: { contextExternalToolId }, - }, + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -427,10 +407,9 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, - content: { contextExternalToolId }, - }, + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -459,15 +438,12 @@ describe("ExternalToolElement", () => { describe("Loading", () => { describe("when the component is loading", () => { const setup = () => { - const contextExternalToolId = "context-external-tool-id"; - useExternalToolElementDisplayStateMock.isLoading = ref(true); const { wrapper } = getWrapper({ - element: { - ...EMPTY_TEST_ELEMENT, - content: { contextExternalToolId }, - }, + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), isEditMode: false, }); @@ -493,10 +469,9 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, - content: { contextExternalToolId }, - }, + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ contextExternalToolId }) @@ -521,7 +496,7 @@ describe("ExternalToolElement", () => { describe("when clicking on a un-configured tool card in edit mode", () => { const setup = () => { const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }); @@ -557,7 +532,7 @@ describe("ExternalToolElement", () => { ); const { wrapper } = getWrapper({ - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }); @@ -603,10 +578,9 @@ describe("ExternalToolElement", () => { describe("when clicking on a configured tool card", () => { const setup = () => { const { wrapper } = getWrapper({ - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }); @@ -657,7 +631,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }, externalToolDisplayDataFactory.build() @@ -689,7 +663,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }, externalToolDisplayDataFactory.build() @@ -718,7 +692,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { - element: EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build(), isEditMode: true, }, externalToolDisplayDataFactory.build({ @@ -747,10 +721,9 @@ describe("ExternalToolElement", () => { jest.useFakeTimers({ legacyFakeTimers: true }); const { wrapper, refreshTime } = getWrapper( { - element: { - ...EMPTY_TEST_ELEMENT, + element: externalToolElementResponseFactory.build({ content: { contextExternalToolId: "contextExternalToolId" }, - }, + }), isEditMode: false, }, externalToolDisplayDataFactory.build({ @@ -780,4 +753,124 @@ describe("ExternalToolElement", () => { ).toHaveBeenCalledTimes(2); }); }); + + describe("Aria label", () => { + describe("when no tool is selected", () => { + const setup = () => { + const { wrapper } = getWrapper({ + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: null }, + }), + isEditMode: true, + }); + + return { + wrapper, + }; + }; + + it("should read that a tool needs to be selected", async () => { + const { wrapper } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + + expect(card.attributes("aria-label")).toEqual( + "components.cardElement.externalToolElement, feature-board-external-tool-element.placeholder.selectTool" + ); + }); + }); + + describe("when a tool is displayed and will be opened in the same tab", () => { + const setup = () => { + const toolName = "testTool"; + + const { wrapper } = getWrapper( + { + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), + isEditMode: true, + }, + externalToolDisplayDataFactory.build({ + name: toolName, + openInNewTab: false, + }) + ); + + return { + wrapper, + toolName, + }; + }; + + it("should read the tool name and the tab it is started in", async () => { + const { wrapper, toolName } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + + expect(card.attributes("aria-label")).toEqual( + `components.cardElement.externalToolElement, ${toolName}, common.ariaLabel.sameTab` + ); + }); + }); + + describe("when a tool is displayed and will be opened in a new tab", () => { + const setup = () => { + const toolName = "testTool"; + + const { wrapper } = getWrapper( + { + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), + isEditMode: true, + }, + externalToolDisplayDataFactory.build({ + name: toolName, + openInNewTab: true, + }) + ); + + return { + wrapper, + toolName, + }; + }; + + it("should read the tool name and the tab it is started in", async () => { + const { wrapper, toolName } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + + expect(card.attributes("aria-label")).toEqual( + `components.cardElement.externalToolElement, ${toolName}, common.ariaLabel.newTab` + ); + }); + }); + + describe("when a tool is selected and currently loading", () => { + const setup = () => { + const { wrapper } = getWrapper({ + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: "contextExternalToolId" }, + }), + isEditMode: true, + }); + + return { + wrapper, + }; + }; + + it("should read that the tool is loading", async () => { + const { wrapper } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + + expect(card.attributes("aria-label")).toEqual( + `components.cardElement.externalToolElement, common.loading.text` + ); + }); + }); + }); }); diff --git a/src/modules/feature/board-external-tool-element/ExternalToolElement.vue b/src/modules/feature/board-external-tool-element/ExternalToolElement.vue index 1a2b67bb37..36912b7281 100644 --- a/src/modules/feature/board-external-tool-element/ExternalToolElement.vue +++ b/src/modules/feature/board-external-tool-element/ExternalToolElement.vue @@ -10,6 +10,7 @@ tabindex="0" role="button" :loading="isLoading" + :aria-label="ariaLabel" @keyup.enter="onClickElement" @keydown.up.down="onKeydownArrow" @keydown.stop @@ -55,7 +56,7 @@ - diff --git a/tests/test-utils/factory/externalToolElementResponseFactory.ts b/tests/test-utils/factory/externalToolElementResponseFactory.ts new file mode 100644 index 0000000000..1045bf9ad1 --- /dev/null +++ b/tests/test-utils/factory/externalToolElementResponseFactory.ts @@ -0,0 +1,16 @@ +import { + ContentElementType, + ExternalToolElementResponse, +} from "@/serverApi/v3"; +import { Factory } from "fishery"; +import { timestampsResponseFactory } from "./timestampsResponseFactory"; + +export const externalToolElementResponseFactory = + Factory.define(({ sequence }) => ({ + id: `external-tool-element-response-${sequence}`, + type: ContentElementType.ExternalTool, + content: { + contextExternalToolId: null, + }, + timestamps: timestampsResponseFactory.build(), + })); diff --git a/tests/test-utils/factory/index.ts b/tests/test-utils/factory/index.ts index 80cfb0efd4..b38763660a 100644 --- a/tests/test-utils/factory/index.ts +++ b/tests/test-utils/factory/index.ts @@ -25,6 +25,7 @@ export * from "./customParameterResponseFactory"; export * from "./drawingElementResponseFactory"; export * from "./envsFactory"; export * from "./externalToolDisplayDataFactory"; +export * from "./externalToolElementResponseFactory"; export * from "./fileElementContentFactory"; export * from "./fileElementResponseFactory"; export * from "./filerecordResponse.factory"; From 930f2931bc81234e96dfb694c4089936c71c152a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= Date: Wed, 18 Sep 2024 16:19:42 +0200 Subject: [PATCH 2/3] improve test cov --- .../ExternalToolElement.unit.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts b/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts index c075df3e9a..fd85798424 100644 --- a/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts +++ b/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts @@ -754,6 +754,30 @@ describe("ExternalToolElement", () => { }); }); + describe("when moving the element with arrow keys", () => { + const setup = () => { + const { wrapper } = getWrapper({ + element: externalToolElementResponseFactory.build({ + content: { contextExternalToolId: null }, + }), + isEditMode: true, + }); + + return { + wrapper, + }; + }; + + it("should emit an event", async () => { + const { wrapper } = setup(); + + const card = wrapper.getComponent({ ref: "externalToolElement" }); + await card.trigger("keydown.up"); + + expect(wrapper.emitted("move-keyboard:edit")).toHaveLength(1); + }); + }); + describe("Aria label", () => { describe("when no tool is selected", () => { const setup = () => { From f4646f331ff4ffce0a25e04f460ca45e35cac69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= Date: Thu, 19 Sep 2024 13:29:37 +0200 Subject: [PATCH 3/3] share id --- .../ExternalToolElement.unit.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts b/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts index fd85798424..f769c66845 100644 --- a/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts +++ b/src/modules/feature/board-external-tool-element/ExternalToolElement.unit.ts @@ -375,7 +375,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { element: externalToolElementResponseFactory.build({ - content: { contextExternalToolId: "contextExternalToolId" }, + content: { contextExternalToolId }, }), isEditMode: false, }, @@ -408,7 +408,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { element: externalToolElementResponseFactory.build({ - content: { contextExternalToolId: "contextExternalToolId" }, + content: { contextExternalToolId }, }), isEditMode: false, }, @@ -470,7 +470,7 @@ describe("ExternalToolElement", () => { const { wrapper } = getWrapper( { element: externalToolElementResponseFactory.build({ - content: { contextExternalToolId: "contextExternalToolId" }, + content: { contextExternalToolId }, }), isEditMode: false, }, @@ -807,16 +807,18 @@ describe("ExternalToolElement", () => { describe("when a tool is displayed and will be opened in the same tab", () => { const setup = () => { const toolName = "testTool"; + const contextExternalToolId = "contextExternalToolId"; const { wrapper } = getWrapper( { element: externalToolElementResponseFactory.build({ - content: { contextExternalToolId: "contextExternalToolId" }, + content: { contextExternalToolId }, }), isEditMode: true, }, externalToolDisplayDataFactory.build({ name: toolName, + contextExternalToolId, openInNewTab: false, }) ); @@ -841,16 +843,18 @@ describe("ExternalToolElement", () => { describe("when a tool is displayed and will be opened in a new tab", () => { const setup = () => { const toolName = "testTool"; + const contextExternalToolId = "contextExternalToolId"; const { wrapper } = getWrapper( { element: externalToolElementResponseFactory.build({ - content: { contextExternalToolId: "contextExternalToolId" }, + content: { contextExternalToolId }, }), isEditMode: true, }, externalToolDisplayDataFactory.build({ name: toolName, + contextExternalToolId, openInNewTab: true, }) );