diff --git a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/EditDropdown.vue b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/EditDropdown.vue index 1059199841..e956b6df06 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/EditDropdown.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/EditDropdown.vue @@ -253,7 +253,7 @@ export default defineComponent({ }), onConfirm: (expirationDateTime: DateTime) => { this.$emit('expirationDateChanged', { - expirationDateTime + expirationDateTime: expirationDateTime?.toISO() ?? null }) } }) diff --git a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue index 6df222a117..4e0a12b6b0 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue @@ -599,8 +599,8 @@ export default defineComponent({ this.selectedRole = role }, - collaboratorExpiryChanged({ expirationDate }: { expirationDate: string }) { - this.expirationDate = expirationDate + collaboratorExpiryChanged({ expirationDate }: { expirationDate: DateTime }) { + this.expirationDate = expirationDate?.toISO() ?? null ;(this.$refs.showMoreShareOptionsDropRef as InstanceType).hide() }, diff --git a/packages/web-app-files/src/components/SideBar/Shares/Links/EditDropdown.vue b/packages/web-app-files/src/components/SideBar/Shares/Links/EditDropdown.vue index e61449defd..8ae8dfc5ef 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/Links/EditDropdown.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/Links/EditDropdown.vue @@ -114,7 +114,7 @@ export default defineComponent({ onConfirm: (expirationDateTime: DateTime) => { emit('updateLink', { linkShare: { ...props.linkShare }, - options: { expirationDateTime } + options: { expirationDateTime: expirationDateTime?.toISO() ?? null } }) } }) diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/EditDropdown.spec.ts b/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/EditDropdown.spec.ts index f3c2b0a898..a91516c722 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/EditDropdown.spec.ts +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/EditDropdown.spec.ts @@ -3,6 +3,7 @@ import { defaultPlugins, PartialComponentProps, shallowMount } from '@opencloud- import { mock } from 'vitest-mock-extended' import { Resource } from '@opencloud-eu/web-client' import { OcButton } from '@opencloud-eu/design-system/components' +import { DateTime } from 'luxon' const selectors = { editBtn: '.collaborator-edit-dropdown-options-btn', @@ -46,6 +47,35 @@ describe('EditDropdown', () => { const { wrapper } = getWrapper({ canEdit: false }) expect(wrapper.find(selectors.expireDateMenuAction).exists()).toBeFalsy() }) + it('emits an ISO string when the date picker confirms a DateTime', () => { + const { wrapper } = getWrapper({ canEdit: true }) + + const dispatchModalSpy = vi.spyOn(wrapper.vm, 'dispatchModal') + wrapper.vm.showDatePickerModal() + + const onConfirm = dispatchModalSpy.mock.calls[0][0].onConfirm + const expirationDateTime = DateTime.fromISO('2026-12-31T00:00:00.000Z') + onConfirm(expirationDateTime) + + expect(wrapper.emitted('expirationDateChanged')).toBeTruthy() + expect(wrapper.emitted('expirationDateChanged')[0][0]).toEqual({ + expirationDateTime: expirationDateTime.toISO() + }) + }) + it('emits null when the date picker confirms with null', () => { + const { wrapper } = getWrapper({ canEdit: true }) + + const dispatchModalSpy = vi.spyOn(wrapper.vm, 'dispatchModal') + wrapper.vm.showDatePickerModal() + + const onConfirm = dispatchModalSpy.mock.calls[0][0].onConfirm + onConfirm(null) + + expect(wrapper.emitted('expirationDateChanged')).toBeTruthy() + expect(wrapper.emitted('expirationDateChanged')[0][0]).toEqual({ + expirationDateTime: null + }) + }) }) describe('navigate to parent action', () => { it('is being rendered when sharedParentRoute is given', () => { diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.spec.ts b/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.spec.ts index caeb2d893b..e7538c825c 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.spec.ts +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.spec.ts @@ -3,6 +3,7 @@ import InviteCollaboratorForm from '../../../../../../../src/components/SideBar/ import { defaultComponentMocks, defaultPlugins, + ocDropStub, RouteLocation, shallowMount } from '@opencloud-eu/web-test-helpers' @@ -17,6 +18,7 @@ import { Group, User } from '@opencloud-eu/web-client/graph/generated' import { OcButton } from '@opencloud-eu/design-system/components' import RoleDropdown from '../../../../../../../src/components/SideBar/Shares/Collaborators/RoleDropdown.vue' import { ShareRoleType } from '../../../../../../../src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue' +import { DateTime } from 'luxon' vi.mock('lodash-es', () => ({ debounce: (fn: any) => fn() })) @@ -145,6 +147,41 @@ describe('InviteCollaboratorForm', () => { expect(addShare).toHaveBeenCalled() }) + it('passes the expiration date set via collaboratorExpiryChanged to addShare', async () => { + const { wrapper } = getWrapper({}, { OcDrop: ocDropStub }) + + wrapper.vm.selectedCollaborators = [mock()] + + const expirationDateTime = DateTime.fromISO('2026-12-31T00:00:00.000Z') + wrapper.vm.collaboratorExpiryChanged({ expirationDate: expirationDateTime }) + + const { addShare } = useSharesStore() + vi.mocked(addShare).mockResolvedValue(undefined) + + await wrapper.vm.$nextTick() + await wrapper.vm.share() + + expect(addShare).toHaveBeenCalledWith( + expect.objectContaining({ + options: expect.objectContaining({ + expirationDateTime: expirationDateTime.toISO() + }) + }) + ) + }) + it('converts DateTime to ISO string in collaboratorExpiryChanged', () => { + const { wrapper } = getWrapper({}, { OcDrop: ocDropStub }) + + const expirationDateTime = DateTime.fromISO('2026-12-31T00:00:00.000Z') + wrapper.vm.collaboratorExpiryChanged({ expirationDate: expirationDateTime }) + + expect(wrapper.vm.expirationDate).toBe(expirationDateTime.toISO()) + }) + it('sets expirationDate to null when DateTime is null', () => { + const { wrapper } = getWrapper({}, { OcDrop: ocDropStub }) + wrapper.vm.collaboratorExpiryChanged({ expirationDate: null }) + expect(wrapper.vm.expirationDate).toBeNull() + }) it.todo('resets focus upon selecting an invitee') }) describe('share role type filter', () => { @@ -177,23 +214,26 @@ describe('InviteCollaboratorForm', () => { }) }) -function getWrapper({ - storageId = 'fake-storage-id', - resource = mock(folderMock), - users = [], - groups = [], - existingCollaborators = [], - externalShareRoles = [], - user = mock({ id: '1' }) -}: { - storageId?: string - resource?: Resource - users?: User[] - groups?: Group[] - existingCollaborators?: CollaboratorShare[] - externalShareRoles?: ShareRole[] - user?: User -} = {}) { +function getWrapper( + { + storageId = 'fake-storage-id', + resource = mock(folderMock), + users = [], + groups = [], + existingCollaborators = [], + externalShareRoles = [], + user = mock({ id: '1' }) + }: { + storageId?: string + resource?: Resource + users?: User[] + groups?: Group[] + existingCollaborators?: CollaboratorShare[] + externalShareRoles?: ShareRole[] + user?: User + } = {}, + additionalStubs: Record = {} +) { const mocks = defaultComponentMocks({ currentRoute: mock({ params: { storageId } }) }) @@ -226,7 +266,7 @@ function getWrapper({ availableInternalShareRoles: [mock()] }, mocks, - stubs: { OcSelect: false, VueSelect: false } + stubs: { OcSelect: false, VueSelect: false, ...additionalStubs } } }) } diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/EditDropdown.spec.ts b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/EditDropdown.spec.ts index 1af6672dfb..f9652fed6e 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/EditDropdown.spec.ts +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Links/EditDropdown.spec.ts @@ -10,10 +10,12 @@ import { mock } from 'vitest-mock-extended' import { AncestorMetaDataValue, useGetMatchingSpace, + useModals, useResourcesStore } from '@opencloud-eu/web-pkg' import { SharingLinkType } from '@opencloud-eu/web-client/graph/generated' import { Resource } from '@opencloud-eu/web-client' +import { DateTime } from 'luxon' vi.mock('@opencloud-eu/web-pkg', async (importOriginal) => ({ ...(await importOriginal()), @@ -58,6 +60,53 @@ describe('EditDropdown component', () => { const { wrapper } = getWrapper() expect(wrapper.vm.editOptions.some((option) => option.id === 'add-expiration')).toBeTruthy() }) + it('emits an ISO string when the date picker confirms a DateTime', () => { + const linkShareWithExpiration = { + ...exampleLink, + expirationDateTime: '2026-12-01T00:00:00.000Z' + } as LinkShare + + const { wrapper } = getWrapper({ linkShare: linkShareWithExpiration }) + const modalsStore = useModals() + const dispatchModalSpy = vi.spyOn(modalsStore, 'dispatchModal') + + const editExpirationOption = wrapper.vm.editOptions.find( + (option) => option.id === 'edit-expiration' + ) + editExpirationOption.method() + + const onConfirm = dispatchModalSpy.mock.calls[0][0].onConfirm + const expirationDateTime = DateTime.fromISO('2026-12-31T00:00:00.000Z') + onConfirm(expirationDateTime) + + expect(wrapper.emitted('updateLink')).toBeTruthy() + expect(wrapper.emitted('updateLink')[0][0]).toMatchObject({ + options: { expirationDateTime: expirationDateTime.toISO() } + }) + }) + it('emits null when the date picker confirms with null', () => { + const linkShareWithExpiration = { + ...exampleLink, + expirationDateTime: '2026-12-01T00:00:00.000Z' + } as LinkShare + + const { wrapper } = getWrapper({ linkShare: linkShareWithExpiration }) + const modalsStore = useModals() + const dispatchModalSpy = vi.spyOn(modalsStore, 'dispatchModal') + + const editExpirationOption = wrapper.vm.editOptions.find( + (option) => option.id === 'edit-expiration' + ) + editExpirationOption.method() + + const onConfirm = dispatchModalSpy.mock.calls[0][0].onConfirm + onConfirm(null) + + expect(wrapper.emitted('updateLink')).toBeTruthy() + expect(wrapper.emitted('updateLink')[0][0]).toMatchObject({ + options: { expirationDateTime: null } + }) + }) }) describe('rename', () => { it('does not contain "rename" option if user cannot rename the link', () => { diff --git a/packages/web-app-search/tests/unit/portals/SearchBar.spec.ts b/packages/web-app-search/tests/unit/portals/SearchBar.spec.ts index 207ea44156..ae4d469f62 100644 --- a/packages/web-app-search/tests/unit/portals/SearchBar.spec.ts +++ b/packages/web-app-search/tests/unit/portals/SearchBar.spec.ts @@ -189,7 +189,7 @@ describe('Search Bar portal component', () => { }) wrapper .findComponent(selectors.searchFilters) - .vm.$emit('update:model-value', { + .vm.$emit('update:modelValue', { value: { id: SearchLocationFilterConstants.currentFolder } }) @@ -208,7 +208,7 @@ describe('Search Bar portal component', () => { const { wrapper } = getMountedWrapper() wrapper .findComponent(selectors.searchFilters) - .vm.$emit('update:model-value', { + .vm.$emit('update:modelValue', { value: { id: SearchLocationFilterConstants.currentFolder } }) diff --git a/packages/web-pkg/src/components/Filters/DateFilter.vue b/packages/web-pkg/src/components/Filters/DateFilter.vue index feab8b6b6f..9334cf6da0 100644 --- a/packages/web-pkg/src/components/Filters/DateFilter.vue +++ b/packages/web-pkg/src/components/Filters/DateFilter.vue @@ -281,7 +281,7 @@ const setDateRangeDate = ( type: 'from' | 'to' ) => { if (error) { - console.error('date could not be set') + console.warn('date could not be set') return } diff --git a/packages/web-pkg/tests/unit/services/client.spec.ts b/packages/web-pkg/tests/unit/services/client.spec.ts index c676cb69c2..db586ec2a6 100644 --- a/packages/web-pkg/tests/unit/services/client.spec.ts +++ b/packages/web-pkg/tests/unit/services/client.spec.ts @@ -24,6 +24,7 @@ const getClientServiceMock = () => { }) } const v4uuid = '00000000-0000-0000-0000-000000000000' +vi.mock('../../../src/http') vi.mock('uuid', () => ({ v4: () => v4uuid })) vi.mock('@opencloud-eu/web-client', async (importOriginal) => ({ ...(await importOriginal()), @@ -42,7 +43,6 @@ describe('ClientService', () => { expect(clientService.httpAuthenticated).toBeInstanceOf(HttpClient) }) it('initializes the http client with baseURL and static headers', () => { - vi.mock('../../../src/http') const mocky = vi.mocked(HttpClient) getClientServiceMock() @@ -61,7 +61,6 @@ describe('ClientService', () => { expect(clientService.httpUnAuthenticated).toBeInstanceOf(HttpClient) }) it('initializes the http client with baseURL and static headers', () => { - vi.mock('../../../src/http') const mocky = vi.mocked(HttpClient) getClientServiceMock()