diff --git a/src/modules/feature/board-file-element/content/FileContent.unit.ts b/src/modules/feature/board-file-element/content/FileContent.unit.ts index 932183fa7f..6a9a159cb4 100644 --- a/src/modules/feature/board-file-element/content/FileContent.unit.ts +++ b/src/modules/feature/board-file-element/content/FileContent.unit.ts @@ -6,8 +6,10 @@ import { FileAlert } from "../shared/types/FileAlert.enum"; import FileContent from "./FileContent.vue"; import FileAlerts from "./alert/FileAlerts.vue"; import FileDisplay from "./display/FileDisplay.vue"; +import FileDescription from "./display/file-description/FileDescription.vue"; import ContentElementFooter from "./footer/ContentElementFooter.vue"; import FileInputs from "./inputs/FileInputs.vue"; +import { BOARD_IS_LIST_LAYOUT } from "@util-board"; describe("FileContent", () => { beforeEach(() => { @@ -19,129 +21,589 @@ describe("FileContent", () => { jest.useRealTimers(); }); - describe("When EditMode is true", () => { - describe("When PreviewUrl is defined", () => { - const setup = () => { - const element = fileElementResponseFactory.build(); + const setup = (options?: { + isListBoard?: boolean; + mimeType?: string; + previewUrl?: string; + windowWidth?: number; + }) => { + const { isListBoard, mimeType, previewUrl, windowWidth } = { + isListBoard: false, + mimeType: "testMimeType", + previewUrl: "testPreviewUrl", + windowWidth: 1280, + ...options, + }; + + Object.defineProperty(window, "innerWidth", { + writable: true, + configurable: true, + value: windowWidth, + }); - const fileProperties = { - name: "test", - size: 100, - url: "test", - previewUrl: "test", - previewStatus: PreviewStatus.PREVIEW_POSSIBLE, - isDownloadAllowed: true, - element, - }; + const element = fileElementResponseFactory.build(); + + const fileProperties = { + name: "fileName", + size: 100, + url: "testUrl", + previewUrl, + previewStatus: PreviewStatus.PREVIEW_POSSIBLE, + isDownloadAllowed: true, + element, + mimeType, + }; + const alerts = [FileAlert.AWAITING_SCAN_STATUS]; + + const wrapper = shallowMount(FileContent, { + props: { + fileProperties, + isEditMode: true, + alerts, + }, + global: { + plugins: [createTestingVuetify()], + provide: { + [BOARD_IS_LIST_LAYOUT as symbol]: isListBoard, + }, + }, + }); - const alerts = [FileAlert.AWAITING_SCAN_STATUS]; - const wrapper = shallowMount(FileContent, { - props: { - fileProperties, - isEditMode: true, - alerts, - }, - global: { plugins: [createTestingVuetify()] }, + return { + wrapper, + fileProperties, + alerts, + }; + }; + + describe("when board is a list board", () => { + describe("when file content is a pdf", () => { + it.each` + screenSize | px + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "content should have row style for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth, + }); + + expect(wrapper.classes()).toContain("flex-row"); + } + ); + + it("content should have column style when display size is smaller than 600px", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth: 599, }); - return { - wrapper, - fileProperties, - alerts, - }; - }; + expect(wrapper.classes()).toContain("flex-column"); + }); + + it.each` + screenSize | px + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "file display container should have a width of 33% for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth, + }); + + const fileDisplayContainer = + wrapper.getComponent(FileDisplay).element.parentElement; + + expect(fileDisplayContainer.classList).toContain("w-33"); + } + ); + + it("file display container should not have a width of 33% when display size is smaller than 600px", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth: 599, + }); - it("should pass props to FileDisplay", () => { - const { wrapper, fileProperties } = setup(); + const fileDisplayContainer = + wrapper.getComponent(FileDisplay).element.parentElement; - const fileContent = wrapper.findComponent(FileDisplay); + expect(fileDisplayContainer.classList).not.toContain("w-33"); + }); - expect(fileContent.props()).toEqual({ - fileProperties, - isEditMode: true, + it.each` + screenSize | px + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "should have class 'file-information' for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth, + }); + + const fileDisplayContainer = + wrapper.getComponent(FileDescription).element.parentElement; + + expect(fileDisplayContainer.classList).toContain("file-information"); + } + ); + + it("should not have class 'file-information' when display size is smaller than 600px", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth: 599, }); + + const fileDisplayContainer = + wrapper.getComponent(FileDescription).element.parentElement; + + expect(fileDisplayContainer.classList).not.toContain( + "file-information" + ); }); + }); + + describe("when file content is not a pdf", () => { + it.each` + screenSize | px + ${"extra small"} | ${599} + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "content should have column style for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + isListBoard: true, + windowWidth, + }); + + expect(wrapper.classes()).toContain("flex-column"); + } + ); + + it.each` + screenSize | px + ${"extra small"} | ${599} + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "file display container should not have a width of 33% for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + isListBoard: true, + windowWidth, + }); + + const fileDisplayContainer = + wrapper.getComponent(FileDisplay).element.parentElement; + + expect(fileDisplayContainer.classList).not.toContain("w-33"); + } + ); + + it.each` + screenSize | px + ${"extra small"} | ${599} + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "should not have class file-information for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + isListBoard: true, + windowWidth, + }); + + const fileDisplayContainer = + wrapper.getComponent(FileDescription).element.parentElement; + + expect(fileDisplayContainer.classList).not.toContain( + "file-information" + ); + } + ); + }); + }); - it("should pass props to ContentElementFooter", () => { - const { wrapper, fileProperties } = setup(); + describe("when board is not a list board", () => { + it.each` + screenSize | px + ${"extra small"} | ${599} + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "content should have column style for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + isListBoard: false, + windowWidth, + }); - const contentElementFooter = - wrapper.findComponent(ContentElementFooter); + expect(wrapper.classes()).toContain("flex-column"); + } + ); + + it.each` + screenSize | px + ${"extra small"} | ${599} + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "file display container should not have a width of 33% for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + isListBoard: false, + windowWidth, + }); - expect(contentElementFooter.props()).toEqual({ - fileProperties, + const fileDisplayContainer = + wrapper.getComponent(FileDisplay).element.parentElement; + + expect(fileDisplayContainer.classList).not.toContain("w-33"); + } + ); + + it.each` + screenSize | px + ${"extra small"} | ${599} + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "should not have class file-information for $screenSize display sizes", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + isListBoard: false, + windowWidth, }); + + const fileDisplayContainer = + wrapper.getComponent(FileDescription).element.parentElement; + + expect(fileDisplayContainer.classList).not.toContain( + "file-information" + ); + } + ); + }); + + describe("file display", () => { + it("should render file display component", () => { + const { wrapper } = setup(); + + const fileDisplay = wrapper.findComponent(FileDisplay); + + expect(fileDisplay.exists()).toBe(true); + }); + + it("should pass props to FileDisplay", () => { + const { wrapper, fileProperties } = setup(); + + const fileContent = wrapper.findComponent(FileDisplay); + + expect(fileContent.props()).toEqual({ + fileProperties, + isEditMode: true, + showMenu: true, }); + }); + + describe("show menu", () => { + it("should pass false when preview file is undefined", () => { + const { wrapper } = setup({ + previewUrl: undefined, + }); - it("should pass props to FileAlert", () => { - const { wrapper, alerts } = setup(); + const props = wrapper.findComponent(FileDisplay).attributes(); - const fileAlert = wrapper.findComponent(FileAlerts); + expect(props.showmenu).toBe("false"); + }); + it("should pass true when preview file is defined", () => { + const { wrapper } = setup(); + + const props = wrapper.findComponent(FileDisplay).attributes(); + + expect(props.showmenu).toBe("true"); + }); - expect(fileAlert.props()).toEqual({ - alerts, + it("should pass true when video file", () => { + const { wrapper } = setup({ + mimeType: "video/mp4", }); + + const props = wrapper.findComponent(FileDisplay).attributes(); + + expect(props.showmenu).toBe("true"); }); - it("should pass props to FileInputs", () => { - const { wrapper, fileProperties } = setup(); + it("should pass true when audio file", () => { + const { wrapper } = setup({ + mimeType: "audio/mp4", + }); + + const props = wrapper.findComponent(FileDisplay).attributes(); - const fileInputs = wrapper.findComponent(FileInputs); + expect(props.showmenu).toBe("true"); + }); - expect(fileInputs.props()).toEqual({ - fileProperties, - isEditMode: true, + it("should pass true when pdf file is not on a listboard", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: false, }); + + const props = wrapper.findComponent(FileDisplay).attributes(); + + expect(props.showmenu).toBe("true"); }); - describe("when file inputs emit update:alternativeText", () => { - it("should emit update:alternativeText", async () => { - const { wrapper } = setup(); + it("should pass true when pdf file is on a listboard with a screensize smaller than 600 px", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth: 599, + }); - const fileInputs = wrapper.findComponent(FileInputs); + const props = wrapper.findComponent(FileDisplay).attributes(); - fileInputs.vm.$emit("update:alternativeText"); - jest.runAllTimers(); + expect(props.showmenu).toBe("true"); + }); - expect(wrapper.emitted("update:alternativeText")).toHaveLength(1); + it("should pass false when pdf file is on a listboard with small or larger screensize", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth: 900, }); + + const props = wrapper.findComponent(FileDisplay).attributes(); + + expect(props.showmenu).toBe("false"); + }); + }); + + it("should emit add:alert event", async () => { + const { wrapper } = setup(); + + const fileDisplay = wrapper.findComponent(FileDisplay); + + fileDisplay.vm.$emit("add:alert"); + + expect(wrapper.emitted("add:alert")).toHaveLength(1); + }); + }); + + describe("file description", () => { + it("should render file description ", () => { + const { wrapper } = setup(); + + const fileDescription = wrapper.findComponent(FileDescription); + + expect(fileDescription.exists()).toBe(true); + }); + + describe("show title", () => { + it("should pass true when pdf file", () => { + const { wrapper } = setup({ mimeType: "application/pdf" }); + + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.showtitle).toBe("true"); + }); + + it("should pass true when not video or audio and previewUrl is undefined", () => { + const { wrapper } = setup({ previewUrl: undefined }); + + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.showtitle).toBe("true"); }); - describe("when file inputs emit update:caption", () => { - it("should emit update:caption event, when it receives update:caption event from file inputs component", async () => { - const { wrapper } = setup(); + it("should pass false when video file", () => { + const { wrapper } = setup({ mimeType: "video/mp4" }); - const fileInputs = wrapper.findComponent(FileInputs); + const props = wrapper.findComponent(FileDescription).attributes(); - fileInputs.vm.$emit("update:caption"); - jest.runAllTimers(); + expect(props.showtitle).toBe("false"); + }); + + it("should pass false when audio file", () => { + const { wrapper } = setup({ mimeType: "audio/mp4" }); + + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.showtitle).toBe("false"); + }); + + it("should pass false when preview url is definied", () => { + const { wrapper } = setup({ previewUrl: "test" }); + + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.showtitle).toBe("false"); + }); + }); - expect(wrapper.emitted("update:caption")).toHaveLength(1); + describe("show menu", () => { + it("should pass true when preview url is not defined", () => { + const { wrapper } = setup({ + previewUrl: undefined, }); + + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.showmenu).toBe("true"); }); - describe("when file alerts emits on-status-reload", () => { - it("should emit fetch:file event", async () => { - const { wrapper } = setup(); + it("should pass true when the file is a PDF and on a listboard with a screensize small or larger", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth: 600, + }); - const fileAlert = wrapper.findComponent(FileAlerts); + const props = wrapper.findComponent(FileDescription).attributes(); - await fileAlert.vm.$emit("on-status-reload"); + expect(props.showmenu).toBe("true"); + }); - expect(wrapper.emitted("fetch:file")).toBeTruthy(); + it("should pass false when the file is a PDF and on a listboard with a xs screensize", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: true, + windowWidth: 599, }); + + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.showmenu).toBe("false"); }); - describe("when file display emits add:alert", () => { - it("should emit add:alert event", async () => { - const { wrapper } = setup(); + it("should pass false when preview url is defined and file is not a pdf on a listbaord", () => { + const { wrapper } = setup(); - const fileDisplay = wrapper.findComponent(FileDisplay); + const props = wrapper.findComponent(FileDescription).attributes(); - fileDisplay.vm.$emit("add:alert"); + expect(props.showmenu).toBe("false"); + }); - expect(wrapper.emitted("add:alert")).toHaveLength(1); + it("should pass false when pdf file is not on a listboard", () => { + const { wrapper } = setup({ + mimeType: "application/pdf", + isListBoard: false, }); + + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.showmenu).toBe("false"); + }); + }); + + describe("file description src", () => { + it("should be undefined when not a pdf file", () => { + const { wrapper } = setup(); + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.src).toBe(undefined); + }); + + it("should pass url to src when pdf file", () => { + const { wrapper, fileProperties } = setup({ + mimeType: "application/pdf", + }); + const props = wrapper.findComponent(FileDescription).attributes(); + + expect(props.src).toBe(fileProperties.url); + }); + }); + }); + + describe("file inputs", () => { + it("should pass props to FileInputs", () => { + const { wrapper, fileProperties } = setup(); + + const fileInputs = wrapper.findComponent(FileInputs); + + expect(fileInputs.props()).toEqual({ + fileProperties, + isEditMode: true, + }); + }); + + it("should emit update:alternativeText", async () => { + const { wrapper } = setup(); + + const fileInputs = wrapper.findComponent(FileInputs); + + fileInputs.vm.$emit("update:alternativeText"); + jest.runAllTimers(); + + expect(wrapper.emitted("update:alternativeText")).toHaveLength(1); + }); + + it("should emit update:caption event, when it receives update:caption event from file inputs component", async () => { + const { wrapper } = setup(); + + const fileInputs = wrapper.findComponent(FileInputs); + + fileInputs.vm.$emit("update:caption"); + jest.runAllTimers(); + + expect(wrapper.emitted("update:caption")).toHaveLength(1); + }); + }); + + describe("content element footer", () => { + it("should pass props to ContentElementFooter", () => { + const { wrapper, fileProperties } = setup(); + + const contentElementFooter = wrapper.findComponent(ContentElementFooter); + + expect(contentElementFooter.props()).toEqual({ + fileProperties, + }); + }); + }); + + describe("file alerts", () => { + it("should pass props to FileAlert", () => { + const { wrapper, alerts } = setup(); + + const fileAlert = wrapper.findComponent(FileAlerts); + + expect(fileAlert.props()).toEqual({ + alerts, + }); + }); + + describe("when file alerts emits on-status-reload", () => { + it("should emit fetch:file event", async () => { + const { wrapper } = setup(); + + const fileAlert = wrapper.findComponent(FileAlerts); + + await fileAlert.vm.$emit("on-status-reload"); + + expect(wrapper.emitted("fetch:file")).toBeTruthy(); }); }); }); diff --git a/src/modules/feature/board-file-element/content/FileContent.vue b/src/modules/feature/board-file-element/content/FileContent.vue index 75d34ea44b..16e4c3c904 100644 --- a/src/modules/feature/board-file-element/content/FileContent.vue +++ b/src/modules/feature/board-file-element/content/FileContent.vue @@ -1,76 +1,155 @@ - + + diff --git a/src/modules/feature/board-file-element/content/display/FileDisplay.unit.ts b/src/modules/feature/board-file-element/content/display/FileDisplay.unit.ts index 8764fb5ddd..440cffff48 100644 --- a/src/modules/feature/board-file-element/content/display/FileDisplay.unit.ts +++ b/src/modules/feature/board-file-element/content/display/FileDisplay.unit.ts @@ -1,8 +1,3 @@ -import { - isAudioMimeType, - isPdfMimeType, - isVideoMimeType, -} from "@/utils/fileHelper"; import { fileElementResponseFactory } from "@@/tests/test-utils"; import { createTestingI18n, @@ -10,16 +5,11 @@ import { } from "@@/tests/test-utils/setup"; import { shallowMount } from "@vue/test-utils"; import AudioDisplay from "./audio-display/AudioDisplay.vue"; -import FileDescription from "./file-description/FileDescription.vue"; import FileDisplay from "./FileDisplay.vue"; import ImageDisplay from "./image-display/ImageDisplay.vue"; import PdfDisplay from "./pdf-display/PdfDisplay.vue"; import VideoDisplay from "./video-display/VideoDisplay.vue"; - -jest.mock("@/utils/fileHelper"); -const isVideoMimeTypeMock = jest.mocked(isVideoMimeType); -const isAudioMimeTypeMock = jest.mocked(isAudioMimeType); -const isPdfMimeTypeMock = jest.mocked(isPdfMimeType); +import { PreviewStatus } from "@/fileStorageApi/v3"; describe("FileDisplay", () => { describe("when previewUrl is defined", () => { @@ -32,16 +22,15 @@ describe("FileDisplay", () => { size: 100, url: "test", previewUrl: "test", - previewStatus: "test", + previewStatus: PreviewStatus.PREVIEW_POSSIBLE, isDownloadAllowed: true, element, + mimeType: "test", }, isEditMode: true, + showMenu: true, }; - isVideoMimeTypeMock.mockReset(); - isVideoMimeTypeMock.mockReturnValueOnce(false); - const wrapper = shallowMount(FileDisplay, { props, global: { plugins: [createTestingVuetify(), createTestingI18n()] }, @@ -73,14 +62,6 @@ describe("FileDisplay", () => { expect(props.iseditmode).toBe("true"); expect(props.element).toBeDefined(); }); - - it("should pass showTitle true to file description", () => { - const { wrapper } = setup(); - - const props = wrapper.findComponent(FileDescription).attributes(); - - expect(props.showtitle).toBe("false"); - }); }); describe("when mimeType is a video type", () => { @@ -92,19 +73,15 @@ describe("FileDisplay", () => { size: 100, url: "test", previewUrl: "test", - previewStatus: "test", + previewStatus: PreviewStatus.PREVIEW_POSSIBLE, isDownloadAllowed: true, element, + mimeType: "video/mp4", }, isEditMode: true, + showMenu: true, }; - isVideoMimeTypeMock.mockReset(); - isVideoMimeTypeMock.mockReturnValueOnce(true); - - isAudioMimeTypeMock.mockReset(); - isAudioMimeTypeMock.mockReturnValueOnce(false); - const wrapper = shallowMount(FileDisplay, { props, global: { plugins: [createTestingVuetify(), createTestingI18n()] }, @@ -137,22 +114,6 @@ describe("FileDisplay", () => { expect(props.iseditmode).toBe("true"); expect(props.element).toBeDefined(); }); - - it("should render file description display component", () => { - const { wrapper } = setup(); - - const fileDescription = wrapper.findComponent(FileDescription); - - expect(fileDescription.exists()).toBe(true); - }); - - it("should pass showTitle true to file description", () => { - const { wrapper } = setup(); - - const props = wrapper.findComponent(FileDescription).attributes(); - - expect(props.showtitle).toBe("false"); - }); }); describe("when mimeType is pdf type", () => { @@ -164,22 +125,15 @@ describe("FileDisplay", () => { size: 100, url: "test", previewUrl: "previewUrl", - previewStatus: "test", + previewStatus: PreviewStatus.PREVIEW_POSSIBLE, isDownloadAllowed: true, element, + mimeType: "application/pdf", }, isEditMode: true, + showMenu: true, }; - isAudioMimeTypeMock.mockReset(); - isAudioMimeTypeMock.mockReturnValueOnce(false); - - isVideoMimeTypeMock.mockReset(); - isVideoMimeTypeMock.mockReturnValueOnce(false); - - isPdfMimeTypeMock.mockReset(); - isPdfMimeTypeMock.mockReturnValueOnce(true); - const wrapper = shallowMount(FileDisplay, { props, global: { plugins: [createTestingVuetify(), createTestingI18n()] }, @@ -194,15 +148,6 @@ describe("FileDisplay", () => { }; }; - it("should pass correct props to file description", () => { - const { wrapper, url } = setup(); - - const props = wrapper.findComponent(FileDescription).attributes(); - - expect(props.src).toBe(url); - expect(props.showtitle).toBeTruthy(); - }); - it("should pass correct props to pdf display component", () => { const { wrapper, fileNameProp, previewUrlProp, srcProp } = setup(); @@ -226,19 +171,15 @@ describe("FileDisplay", () => { size: 100, url: "test", previewUrl: undefined, - previewStatus: "test", + previewStatus: PreviewStatus.PREVIEW_POSSIBLE, isDownloadAllowed: true, element, + mimeType: "video/mp4", }, isEditMode: true, + showMenu: true, }; - isVideoMimeTypeMock.mockReset(); - isVideoMimeTypeMock.mockReturnValueOnce(true); - - isAudioMimeTypeMock.mockReset(); - isAudioMimeTypeMock.mockReturnValueOnce(false); - const wrapper = shallowMount(FileDisplay, { props, global: { plugins: [createTestingVuetify(), createTestingI18n()] }, @@ -268,14 +209,6 @@ describe("FileDisplay", () => { expect(props.src).toBe(url); expect(props.name).toBe(fileNameProp); }); - - it("should pass showTitle false to file description", () => { - const { wrapper } = setup(); - - const props = wrapper.findComponent(FileDescription).attributes(); - - expect(props.showtitle).toBe("false"); - }); }); describe("when mimeType is a audio type", () => { @@ -286,19 +219,15 @@ describe("FileDisplay", () => { name: "test", size: 100, url: "test", - previewStatus: "test", + previewStatus: PreviewStatus.PREVIEW_POSSIBLE, isDownloadAllowed: true, element, + mimeType: "audio/mp3", }, isEditMode: true, + showMenu: true, }; - isVideoMimeTypeMock.mockReset(); - isVideoMimeTypeMock.mockReturnValueOnce(false); - - isAudioMimeTypeMock.mockReset(); - isAudioMimeTypeMock.mockReturnValueOnce(true); - const wrapper = shallowMount(FileDisplay, { props, global: { plugins: [createTestingVuetify(), createTestingI18n()] }, @@ -326,14 +255,6 @@ describe("FileDisplay", () => { expect(props.src).toBe(srcProp); }); - - it("should pass showTitle false to file description", () => { - const { wrapper } = setup(); - - const props = wrapper.findComponent(FileDescription).attributes(); - - expect(props.showtitle).toBe("false"); - }); }); describe("when mimeType is pdf type", () => { @@ -345,22 +266,15 @@ describe("FileDisplay", () => { size: 100, url: "test", previewUrl: undefined, - previewStatus: "test", + previewStatus: PreviewStatus.PREVIEW_POSSIBLE, isDownloadAllowed: true, element, + mimeType: "application/pdf", }, isEditMode: true, + showMenu: true, }; - isAudioMimeTypeMock.mockReset(); - isAudioMimeTypeMock.mockReturnValueOnce(false); - - isVideoMimeTypeMock.mockReset(); - isVideoMimeTypeMock.mockReturnValueOnce(false); - - isPdfMimeTypeMock.mockReset(); - isPdfMimeTypeMock.mockReturnValueOnce(true); - const wrapper = shallowMount(FileDisplay, { props, global: { plugins: [createTestingVuetify(), createTestingI18n()] }, @@ -375,14 +289,6 @@ describe("FileDisplay", () => { }; }; - it("should pass correct props to file description", () => { - const { wrapper, url } = setup(); - - const props = wrapper.findComponent(FileDescription).attributes(); - - expect(props.src).toBe(url); - }); - it("should not render pdf display", () => { const { wrapper } = setup(); @@ -401,22 +307,15 @@ describe("FileDisplay", () => { size: 100, url: "test", previewUrl: undefined, - previewStatus: "test", + previewStatus: PreviewStatus.PREVIEW_POSSIBLE, isDownloadAllowed: true, element, + mimeType: "test", }, isEditMode: true, + showMenu: true, }; - isVideoMimeTypeMock.mockReset(); - isVideoMimeTypeMock.mockReturnValueOnce(false); - - isAudioMimeTypeMock.mockReset(); - isAudioMimeTypeMock.mockReturnValueOnce(false); - - isPdfMimeTypeMock.mockReset(); - isPdfMimeTypeMock.mockReturnValueOnce(false); - const wrapper = shallowMount(FileDisplay, { props, global: { plugins: [createTestingVuetify(), createTestingI18n()] }, @@ -427,22 +326,6 @@ describe("FileDisplay", () => { }; }; - it("should pass showTitle true to file description", () => { - const { wrapper } = setup(); - - const props = wrapper.findComponent(FileDescription).attributes(); - - expect(props.showtitle).toBeTruthy(); - }); - - it("should pass src to file description", () => { - const { wrapper } = setup(); - - const props = wrapper.findComponent(FileDescription).attributes(); - - expect(props.src).toBe(undefined); - }); - it("should not render image display component", () => { const { wrapper } = setup(); diff --git a/src/modules/feature/board-file-element/content/display/FileDisplay.vue b/src/modules/feature/board-file-element/content/display/FileDisplay.vue index f25a13cb8f..b6d9b8c5dd 100644 --- a/src/modules/feature/board-file-element/content/display/FileDisplay.vue +++ b/src/modules/feature/board-file-element/content/display/FileDisplay.vue @@ -1,133 +1,80 @@ - diff --git a/src/modules/feature/board-file-element/content/display/audio-display/AudioDisplay.unit.ts b/src/modules/feature/board-file-element/content/display/audio-display/AudioDisplay.unit.ts index b3868d56e5..e0671d89b9 100644 --- a/src/modules/feature/board-file-element/content/display/audio-display/AudioDisplay.unit.ts +++ b/src/modules/feature/board-file-element/content/display/audio-display/AudioDisplay.unit.ts @@ -6,7 +6,7 @@ import { createMock } from "@golevelup/ts-jest"; import { mdiPause, mdiPlay } from "@mdi/js"; import { mount } from "@vue/test-utils"; import { useMediaControls } from "@vueuse/core"; -import { ref } from "vue"; +import { nextTick, ref } from "vue"; import AudioDisplay from "./AudioDisplay.vue"; jest.mock("@vueuse/core", () => { @@ -20,11 +20,12 @@ jest.mock("@vueuse/core", () => { describe("AudioDisplay", () => { describe("when audio is not playing", () => { - const setup = () => { + const setup = (options?: { showMenu?: boolean }) => { const src = "test-source"; const slotContent = "test-slot-content"; const props = { src, + showMenu: options?.showMenu ?? true, }; const currentTimeRef = ref(0); @@ -68,12 +69,18 @@ describe("AudioDisplay", () => { }; }; - it("should render slot content", () => { - const { wrapper } = setup(); + it("should render slot content if showMenu is true", () => { + const { wrapper } = setup({ showMenu: true }); expect(wrapper.text()).toContain("test-slot-content"); }); + it("should render slot content if showMenu is false", () => { + const { wrapper } = setup({ showMenu: false }); + + expect(wrapper.text()).not.toContain("test-slot-content"); + }); + it("should call onSourceError", () => { const { onSourceErrorMock } = setup(); @@ -129,9 +136,9 @@ describe("AudioDisplay", () => { const playButton = wrapper.findComponent({ name: "v-btn" }); - playButton.vm.$emit("click"); + playButton.trigger("click"); - await wrapper.vm.$nextTick(); + await nextTick(); expect(playingRef.value).toBe(true); }); @@ -143,9 +150,9 @@ describe("AudioDisplay", () => { const audioSlider = wrapper.findComponent({ name: "v-slider" }); - audioSlider.vm.$emit("update:model-value", 10); + audioSlider.setValue(10); - await wrapper.vm.$nextTick(); + await nextTick(); expect(currentTimeRef.value).toBe(10); }); @@ -166,7 +173,7 @@ describe("AudioDisplay", () => { const speedMenu = wrapper.findComponent({ name: "SpeedMenu" }); speedMenu.vm.$emit("updateRate", newRateValue); - await wrapper.vm.$nextTick(); + await nextTick(); expect(rateRef.value).toBe(newRateValue); }); @@ -179,6 +186,7 @@ describe("AudioDisplay", () => { const slotContent = "test-slot-content"; const props = { src, + showMenu: true, }; const currentTimeRef = ref(5); @@ -238,9 +246,9 @@ describe("AudioDisplay", () => { const { wrapper, playingRef } = setup(); const playButton = wrapper.findComponent({ name: "v-btn" }); - playButton.vm.$emit("click"); + playButton.trigger("click"); - await wrapper.vm.$nextTick(); + await nextTick(); expect(playingRef.value).toBe(false); }); diff --git a/src/modules/feature/board-file-element/content/display/audio-display/AudioDisplay.vue b/src/modules/feature/board-file-element/content/display/audio-display/AudioDisplay.vue index 9ade260f41..a0a0d37364 100644 --- a/src/modules/feature/board-file-element/content/display/audio-display/AudioDisplay.vue +++ b/src/modules/feature/board-file-element/content/display/audio-display/AudioDisplay.vue @@ -51,7 +51,7 @@ - + @@ -69,6 +69,7 @@ export default defineComponent({ components: { ContentElementBar, SpeedMenu }, props: { src: { type: String, required: true }, + showMenu: { type: Boolean, required: true }, }, emits: ["error"], setup(props, { emit }) { diff --git a/src/modules/feature/board-file-element/content/display/image-display/ImageDisplay.unit.ts b/src/modules/feature/board-file-element/content/display/image-display/ImageDisplay.unit.ts index d76defa3fb..33bcd51182 100644 --- a/src/modules/feature/board-file-element/content/display/image-display/ImageDisplay.unit.ts +++ b/src/modules/feature/board-file-element/content/display/image-display/ImageDisplay.unit.ts @@ -28,6 +28,7 @@ describe("ImageDisplay", () => { name: "file-record #1.txt", isEditMode: props.isEditMode, element, + showMenu: true, }; const isLightBoxOpen = ref(false); diff --git a/src/modules/feature/board-file-element/content/display/image-display/ImageDisplay.vue b/src/modules/feature/board-file-element/content/display/image-display/ImageDisplay.vue index 4f98f41f76..93c3c9ef80 100644 --- a/src/modules/feature/board-file-element/content/display/image-display/ImageDisplay.vue +++ b/src/modules/feature/board-file-element/content/display/image-display/ImageDisplay.vue @@ -20,7 +20,7 @@ - - + @@ -29,6 +29,7 @@ export default defineComponent({ name: { type: String, required: true }, previewSrc: { type: String, required: true }, src: { type: String, required: true }, + showMenu: { type: Boolean, required: true }, }, components: { ContentElementBar, PreviewImage }, setup(props) { diff --git a/src/modules/feature/board-file-element/content/display/video-display/VideoDisplay.unit.ts b/src/modules/feature/board-file-element/content/display/video-display/VideoDisplay.unit.ts index 9a432dc184..02a39e90d2 100644 --- a/src/modules/feature/board-file-element/content/display/video-display/VideoDisplay.unit.ts +++ b/src/modules/feature/board-file-element/content/display/video-display/VideoDisplay.unit.ts @@ -3,13 +3,14 @@ import VideoDisplay from "./VideoDisplay.vue"; import { createTestingVuetify } from "@@/tests/test-utils/setup"; describe("VideoDisplay", () => { - const setup = () => { + const setup = (options?: { showMenu?: boolean }) => { const src = "test-source"; const name = "test-name"; const slotContent = "test-slot-content"; const props = { src, name, + showMenu: options?.showMenu ?? true, }; const wrapper = mount(VideoDisplay, { @@ -26,6 +27,7 @@ describe("VideoDisplay", () => { wrapper, src, name, + slotContent, }; }; @@ -47,10 +49,16 @@ describe("VideoDisplay", () => { expect(video.attributes("loading")).toBe("lazy"); }); - it("should render with slot content", () => { - const { wrapper } = setup(); + it("should render with slot content if showMenu is true", () => { + const { wrapper, slotContent } = setup({ showMenu: true }); + + expect(wrapper.text()).toContain(slotContent); + }); + + it("should not render with slot content if showMenu is false", () => { + const { wrapper, slotContent } = setup({ showMenu: false }); - expect(wrapper.text()).toContain("test-slot-content"); + expect(wrapper.text()).not.toContain(slotContent); }); describe("when video dispatches error event", () => { diff --git a/src/modules/feature/board-file-element/content/display/video-display/VideoDisplay.vue b/src/modules/feature/board-file-element/content/display/video-display/VideoDisplay.vue index df32043745..12ab0e7621 100644 --- a/src/modules/feature/board-file-element/content/display/video-display/VideoDisplay.vue +++ b/src/modules/feature/board-file-element/content/display/video-display/VideoDisplay.vue @@ -11,7 +11,7 @@ v-on:error="onError" /> - + @@ -26,6 +26,7 @@ export default defineComponent({ props: { src: { type: String, required: true }, name: { type: String, required: true }, + showMenu: { type: Boolean, required: true }, }, components: { ContentElementBar }, emits: ["error"], diff --git a/src/modules/feature/board-file-element/content/inputs/FileInputs.unit.ts b/src/modules/feature/board-file-element/content/inputs/FileInputs.unit.ts index 6b7dff6175..91cb40bfc9 100644 --- a/src/modules/feature/board-file-element/content/inputs/FileInputs.unit.ts +++ b/src/modules/feature/board-file-element/content/inputs/FileInputs.unit.ts @@ -1,5 +1,4 @@ import { PreviewStatus } from "@/fileStorageApi/v3"; -import { isPdfMimeType } from "@/utils/fileHelper"; import { fileElementResponseFactory } from "@@/tests/test-utils"; import { createTestingVuetify } from "@@/tests/test-utils/setup"; import { shallowMount } from "@vue/test-utils"; @@ -7,9 +6,6 @@ import FileInputs from "./FileInputs.vue"; import AlternativeText from "./alternative-text/AlternativeText.vue"; import CaptionText from "./caption/CaptionText.vue"; -jest.mock("@/utils/fileHelper"); -const isPdfMimeTypeMock = jest.mocked(isPdfMimeType); - describe("FileInputs", () => { const setup = (props: { previewUrl?: string; @@ -25,11 +21,9 @@ describe("FileInputs", () => { previewStatus: PreviewStatus.PREVIEW_POSSIBLE, isDownloadAllowed: true, element, + mimeType: props.isPdf ? "application/pdf" : "image/png", }; - isPdfMimeTypeMock.mockReset(); - isPdfMimeTypeMock.mockReturnValueOnce(props.isPdf ?? false); - const wrapper = shallowMount(FileInputs, { props: { fileProperties, isEditMode: props.isEditMode }, global: { plugins: [createTestingVuetify()] }, diff --git a/src/modules/feature/board-file-element/content/inputs/FileInputs.vue b/src/modules/feature/board-file-element/content/inputs/FileInputs.vue index 0ace6f1a4a..4ad4179b7d 100644 --- a/src/modules/feature/board-file-element/content/inputs/FileInputs.vue +++ b/src/modules/feature/board-file-element/content/inputs/FileInputs.vue @@ -14,38 +14,27 @@ - diff --git a/src/modules/feature/board-link-element/components/LinkContentElementDisplay.unit.ts b/src/modules/feature/board-link-element/components/LinkContentElementDisplay.unit.ts index 8c92353d1c..d11dec6d9e 100644 --- a/src/modules/feature/board-link-element/components/LinkContentElementDisplay.unit.ts +++ b/src/modules/feature/board-link-element/components/LinkContentElementDisplay.unit.ts @@ -4,6 +4,7 @@ import { createTestingVuetify, createTestingI18n, } from "@@/tests/test-utils/setup"; +import { BOARD_IS_LIST_LAYOUT } from "@util-board"; type Props = { url?: string; @@ -13,37 +14,171 @@ type Props = { }; describe("LinkContentElementDisplay", () => { - const setup = (props: Props) => { + const setup = (options: { + props: Props; + isListBoard?: boolean; + windowWidth?: number; + }) => { + Object.defineProperty(window, "innerWidth", { + writable: true, + configurable: true, + value: options.windowWidth ?? 1280, + }); + const wrapper = mount(LinkContentElementDisplay, { - global: { plugins: [createTestingVuetify(), createTestingI18n()] }, + global: { + plugins: [createTestingVuetify(), createTestingI18n()], + provide: { + [BOARD_IS_LIST_LAYOUT as symbol]: options.isListBoard ?? false, + }, + }, props: { url: "", title: "", isEditMode: false, - ...props, + ...options.props, }, }); return { wrapper }; }; - describe("when valid url was given", () => { - it("should display the hostname", async () => { - const { wrapper } = setup({ + it("should display the hostname", async () => { + const { wrapper } = setup({ + props: { url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus" }, + }); + + const hostname = "www.zdf.de"; + expect(wrapper.text()).toEqual(hostname); + }); + + it("should display a title", async () => { + const title = "Die Sendung mit der Maus"; + const { wrapper } = setup({ + props: { + url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", + title, + }, + }); + + expect(wrapper.html()).toEqual(expect.stringContaining(title)); + }); + + it("should display the image when image url is given", async () => { + const imageUrl = "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus.jpg"; + const { wrapper } = setup({ + props: { + url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", + imageUrl, + }, + }); + + const image = wrapper.find("img"); + + expect(image.attributes("src")).toEqual(imageUrl); + expect(image.attributes("alt")).toEqual(""); + }); + + it("should not display the image when image url is not given", async () => { + const { wrapper } = setup({ + props: { url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", + }, + }); + + const image = wrapper.find("img"); + + expect(image.exists()).toBe(false); + }); + + describe("when board is a list board", () => { + it.each` + screenSize | px + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "content should have row style for $screenSize display sizes when image url is given", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + props: { + url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", + imageUrl: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus.jpg", + }, + isListBoard: true, + windowWidth, + }); + + expect(wrapper.find(".content-element-bar").classes()).toContain( + "flex-row" + ); + } + ); + + it("content should have column style when display size is smaller than 600px", () => { + const { wrapper } = setup({ + props: { + url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", + imageUrl: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus.jpg", + }, + isListBoard: true, + windowWidth: 599, }); - const hostname = "www.zdf.de"; - expect(wrapper.text()).toEqual(hostname); + expect(wrapper.find(".content-element-bar").classes()).toContain( + "flex-column" + ); }); - it("should display a title", async () => { - const title = "Die Sendung mit der Maus"; + it("content should have column style when no imageUrl is given", () => { const { wrapper } = setup({ - url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", - title, + props: { + url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", + }, + isListBoard: true, + }); + + expect(wrapper.find(".content-element-bar").classes()).toContain( + "flex-column" + ); + }); + }); + + describe("when board is not a list board", () => { + it.each` + screenSize | px + ${"xs"} | ${590} + ${"small"} | ${600} + ${"medium"} | ${960} + ${"large"} | ${1280} + `( + "content should have column style for $screenSize display sizes when image url is given", + ({ px: windowWidth }) => { + const { wrapper } = setup({ + props: { + url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", + imageUrl: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus.jpg", + }, + isListBoard: false, + windowWidth, + }); + + expect(wrapper.find(".content-element-bar").classes()).toContain( + "flex-column" + ); + } + ); + + it("content should have column style when no imageUrl is given", () => { + const { wrapper } = setup({ + props: { + url: "https://www.zdf.de/die-maus/2023-12-06-der-nikolaus", + }, + isListBoard: false, }); - expect(wrapper.html()).toEqual(expect.stringContaining(title)); + expect(wrapper.find(".content-element-bar").classes()).toContain( + "flex-column" + ); }); }); }); diff --git a/src/modules/feature/board-link-element/components/LinkContentElementDisplay.vue b/src/modules/feature/board-link-element/components/LinkContentElementDisplay.vue index ba87d1d06d..e92b58cb35 100644 --- a/src/modules/feature/board-link-element/components/LinkContentElementDisplay.vue +++ b/src/modules/feature/board-link-element/components/LinkContentElementDisplay.vue @@ -4,9 +4,18 @@ ref="linkContentElementDisplay" tabindex="-1" > - + - diff --git a/src/modules/feature/board/board/Board.vue b/src/modules/feature/board/board/Board.vue index eee2070417..dbd1d79ad6 100644 --- a/src/modules/feature/board/board/Board.vue +++ b/src/modules/feature/board/board/Board.vue @@ -119,12 +119,16 @@ import { } from "@data-board"; import { ConfirmationDialog } from "@ui-confirmation-dialog"; import { LightBox } from "@ui-light-box"; -import { extractDataAttribute, useBoardNotifier } from "@util-board"; +import { + extractDataAttribute, + useBoardNotifier, + BOARD_IS_LIST_LAYOUT, +} from "@util-board"; import { useTouchDetection } from "@util-device-detection"; import { useDebounceFn } from "@vueuse/core"; import { SortableEvent } from "sortablejs"; import { Sortable } from "sortablejs-vue3"; -import { computed, onMounted, onUnmounted, watch } from "vue"; +import { computed, onMounted, onUnmounted, provide, watch } from "vue"; import { useI18n } from "vue-i18n"; import { useRouter } from "vue-router"; import AddElementDialog from "../shared/AddElementDialog.vue"; @@ -300,6 +304,8 @@ const isListBoard = computed( board.value?.layout === BoardLayout.List ); +provide(BOARD_IS_LIST_LAYOUT, isListBoard); + const copyResultModalItems = computed( () => copyModule.getCopyResultFailedItems ); diff --git a/src/modules/ui/board/content-element/ContentElementBar.vue b/src/modules/ui/board/content-element/ContentElementBar.vue index 24f739249f..186f59f259 100644 --- a/src/modules/ui/board/content-element/ContentElementBar.vue +++ b/src/modules/ui/board/content-element/ContentElementBar.vue @@ -1,10 +1,23 @@