From c2bcc62bc9b75f8a6bfa7d135ddef932bab5aef0 Mon Sep 17 00:00:00 2001 From: lethemanh Date: Tue, 28 Oct 2025 15:24:27 +0700 Subject: [PATCH 1/4] feat: Implement duplicating files in shared drive :sparkles: --- src/components/FolderPicker/types.ts | 12 ++++- src/declarations.d.ts | 15 ++++++ .../duplicate/components/DuplicateModal.tsx | 45 +++++++++++++++-- src/modules/navigation/AppRoute.jsx | 2 + .../components/SharedDriveFolderBody.jsx | 3 +- .../views/Folder/FolderDuplicateView.tsx | 3 ++ .../views/Folder/SharedDriveDuplicateView.tsx | 49 +++++++++++++++++++ 7 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 src/modules/views/Folder/SharedDriveDuplicateView.tsx diff --git a/src/components/FolderPicker/types.ts b/src/components/FolderPicker/types.ts index 2ce25edb09..b63a74aa7a 100644 --- a/src/components/FolderPicker/types.ts +++ b/src/components/FolderPicker/types.ts @@ -1,6 +1,15 @@ import { IOCozyFile, NextcloudFile } from 'cozy-client/types/types' -export type File = IOCozyFile | NextcloudFile +export type File = (IOCozyFile | NextcloudFile) & { + cozyMetadata?: { + createdOn?: string + } + attributes?: { + cozyMetadata?: { + createdOn?: string + } + } +} export interface FolderPickerEntry { _id?: string @@ -11,4 +20,5 @@ export interface FolderPickerEntry { dir_id?: string class?: string path?: string + driveId?: string } diff --git a/src/declarations.d.ts b/src/declarations.d.ts index 692a21f7ba..c5cf42405e 100644 --- a/src/declarations.d.ts +++ b/src/declarations.d.ts @@ -101,6 +101,21 @@ declare module 'cozy-client/dist/models/file' { filename: string, driveId: string ) => Promise + export const moveRelateToSharedDrive: ( + client: import('cozy-client/types/CozyClient').CozyClient, + source: { + instance?: string + sharing_id?: string + file_id?: string + dir_id?: string + }, + dest: { + instance?: string + sharing_id?: string + dir_id: string + }, + isCopy?: boolean + ) => Promise } declare module 'cozy-client/dist/models/note' { diff --git a/src/modules/duplicate/components/DuplicateModal.tsx b/src/modules/duplicate/components/DuplicateModal.tsx index 34f58d8b15..ea676bbc55 100644 --- a/src/modules/duplicate/components/DuplicateModal.tsx +++ b/src/modules/duplicate/components/DuplicateModal.tsx @@ -2,7 +2,12 @@ import React, { FC, useState } from 'react' import { useNavigate } from 'react-router-dom' import { useClient } from 'cozy-client' -import { copy } from 'cozy-client/dist/models/file' +import { + copy, + isDirectory, + moveRelateToSharedDrive +} from 'cozy-client/dist/models/file' +import { IOCozyFile } from 'cozy-client/types/types' import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert' import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n' @@ -19,6 +24,8 @@ interface DuplicateModalProps { onClose: () => void | Promise showNextcloudFolder?: boolean isPublic?: boolean + showSharedDriveFolder?: boolean + driveId?: string } const DuplicateModal: FC = ({ @@ -26,7 +33,9 @@ const DuplicateModal: FC = ({ currentFolder, onClose, showNextcloudFolder, - isPublic + isPublic, + showSharedDriveFolder, + driveId }) => { const { t } = useI18n() const { showAlert } = useAlert() @@ -41,7 +50,7 @@ const DuplicateModal: FC = ({ setBusy(true) await Promise.all( entries.map(async entry => { - await registerCancelable(copy(client, entry as Partial, folder)) + await registerCancelable(handleCopyApi(client, entry, folder)) }) ) @@ -75,6 +84,35 @@ const DuplicateModal: FC = ({ } } + const handleCopyApi = async ( + client: unknown, + entry: FolderPickerEntry, + folder: File + ): Promise => { + if (driveId || entry.driveId || (folder as IOCozyFile).driveId) { + return await moveRelateToSharedDrive( + client, + { + instance: entry.driveId + ? currentFolder.attributes?.cozyMetadata?.createdOn + : '', + file_id: !isDirectory(entry) ? entry._id : '', + dir_id: isDirectory(entry) ? entry._id : '', + sharing_id: entry.driveId + }, + { + instance: (folder as IOCozyFile).driveId + ? folder.cozyMetadata?.createdOn + : '', + sharing_id: (folder as IOCozyFile).driveId, + dir_id: folder._id + }, + true + ) + } + return await copy(client, entry, folder) + } + /** * The content from nextcloud queries must be refreshed when coping files * This is only a proxy to Nextcloud queries so we don't have real-time or mutations updates @@ -90,6 +128,7 @@ const DuplicateModal: FC = ({ return ( ( } /> } /> } /> + } /> ) : null} diff --git a/src/modules/shareddrives/components/SharedDriveFolderBody.jsx b/src/modules/shareddrives/components/SharedDriveFolderBody.jsx index 2ab2497c63..157f6e5036 100644 --- a/src/modules/shareddrives/components/SharedDriveFolderBody.jsx +++ b/src/modules/shareddrives/components/SharedDriveFolderBody.jsx @@ -12,6 +12,7 @@ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n' import { useModalContext } from '@/lib/ModalContext' import { download, infos, versions, rename, trash, hr } from '@/modules/actions' +import { duplicateTo } from '@/modules/actions/components/duplicateTo' import { moveTo } from '@/modules/actions/components/moveTo' import { FolderBody } from '@/modules/folder/components/FolderBody' @@ -53,7 +54,7 @@ const SharedDriveFolderBody = ({ refresh } const actions = makeActions( - [download, hr, rename, moveTo, infos, hr, versions, hr, trash], + [download, hr, rename, moveTo, duplicateTo, infos, hr, versions, hr, trash], actionsOptions ) diff --git a/src/modules/views/Folder/FolderDuplicateView.tsx b/src/modules/views/Folder/FolderDuplicateView.tsx index d6c94508c5..eb052ba46f 100644 --- a/src/modules/views/Folder/FolderDuplicateView.tsx +++ b/src/modules/views/Folder/FolderDuplicateView.tsx @@ -8,6 +8,7 @@ import flag from 'cozy-flags' import { LoaderModal } from '@/components/LoaderModal' import useDisplayedFolder from '@/hooks/useDisplayedFolder' import { DuplicateModal } from '@/modules/duplicate/components/DuplicateModal' +import { useSharedDrives } from '@/modules/shareddrives/hooks/useSharedDrives' import { buildParentsByIdsQuery } from '@/queries' const FolderDuplicateView: FC = () => { @@ -16,6 +17,7 @@ const FolderDuplicateView: FC = () => { state: { fileIds?: string[] } } const { displayedFolder } = useDisplayedFolder() + const { sharedDrives } = useSharedDrives() const hasFileIds = state.fileIds != undefined @@ -39,6 +41,7 @@ const FolderDuplicateView: FC = () => { return ( 0} currentFolder={displayedFolder} entries={fileResult.data} onClose={onClose} diff --git a/src/modules/views/Folder/SharedDriveDuplicateView.tsx b/src/modules/views/Folder/SharedDriveDuplicateView.tsx new file mode 100644 index 0000000000..ffe5c1a785 --- /dev/null +++ b/src/modules/views/Folder/SharedDriveDuplicateView.tsx @@ -0,0 +1,49 @@ +import React, { FC } from 'react' +import { Navigate, useLocation, useNavigate } from 'react-router-dom' + +import flag from 'cozy-flags' + +import { LoaderModal } from '@/components/LoaderModal' +import useDisplayedFolder from '@/hooks/useDisplayedFolder' +import { DuplicateModal } from '@/modules/duplicate/components/DuplicateModal' +import { useQueryMultipleSharedDriveFolders } from '@/modules/shareddrives/hooks/useQueryMultipleSharedDriveFolders' + +const SharedDriveDuplicateView: FC = () => { + const navigate = useNavigate() + const { state } = useLocation() as { + state: { fileIds?: string[] } + } + const { displayedFolder } = useDisplayedFolder() + + const hasFileIds = state.fileIds != undefined + + const { sharedDriveResults } = useQueryMultipleSharedDriveFolders({ + folderIds: state.fileIds ?? [], + driveId: displayedFolder?.driveId ?? '' + }) + + if (!hasFileIds) { + return + } + + if (sharedDriveResults && displayedFolder) { + const onClose = (): void => { + navigate('..', { replace: true }) + } + + return ( + + ) + } + + return +} + +export { SharedDriveDuplicateView } From ed9c1167aa38b5ac3f91caaa6be6302b649f8adb Mon Sep 17 00:00:00 2001 From: lethemanh Date: Wed, 29 Oct 2025 14:18:23 +0700 Subject: [PATCH 2/4] feat: Integrate copy by shortcuts in shared drive :sparkles: --- src/contexts/ClipboardProvider.tsx | 14 ++++-- src/hooks/useKeyboardShortcuts.tsx | 16 ++++++- .../duplicate/components/DuplicateModal.tsx | 44 +++---------------- src/modules/paste/index.js | 39 +++++++++++++++- .../SharedDrive/SharedDriveFolderView.jsx | 2 +- 5 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/contexts/ClipboardProvider.tsx b/src/contexts/ClipboardProvider.tsx index 17a76f813e..29dc5527fe 100644 --- a/src/contexts/ClipboardProvider.tsx +++ b/src/contexts/ClipboardProvider.tsx @@ -32,7 +32,11 @@ interface ClipboardState { interface ClipboardContextValue { clipboardData: ClipboardState - copyFiles: (files: IOCozyFile[], sourceFolderIds?: Set) => void + copyFiles: ( + files: IOCozyFile[], + sourceFolderIds?: Set, + sourceDirectory?: IOCozyFile + ) => void cutFiles: ( files: IOCozyFile[], sourceFolderIds?: Set, @@ -170,10 +174,14 @@ const ClipboardProvider: React.FC = ({ children }) => { const [state, dispatch] = useReducer(clipboardReducer, initialState) const copyFiles = useCallback( - (files: IOCozyFile[], sourceFolderIds?: Set) => { + ( + files: IOCozyFile[], + sourceFolderIds?: Set, + sourceDirectory?: IOCozyFile + ) => { dispatch({ type: COPY_FILES, - payload: { files, sourceFolderIds } + payload: { files, sourceFolderIds, sourceDirectory } }) }, [] diff --git a/src/hooks/useKeyboardShortcuts.tsx b/src/hooks/useKeyboardShortcuts.tsx index 14ce0be6b4..507216fbbe 100644 --- a/src/hooks/useKeyboardShortcuts.tsx +++ b/src/hooks/useKeyboardShortcuts.tsx @@ -108,14 +108,26 @@ export const useKeyboardShortcuts = ({ return } - copyFiles(filesToCopy, new Set(parentFolderIds)) + copyFiles( + filesToCopy, + new Set(parentFolderIds), + currentFolder as IOCozyFile + ) const message = filesToCopy.length === 1 ? t('alert.item_copied') : t('alert.items_copied', { count: filesToCopy.length }) showAlert({ message, severity: 'success' }) clearSelection() - }, [allowCopy, selectedItems, copyFiles, showAlert, t, clearSelection]) + }, [ + allowCopy, + selectedItems, + currentFolder, + copyFiles, + showAlert, + t, + clearSelection + ]) const handleCut = useCallback(() => { if (!selectedItems.length) return diff --git a/src/modules/duplicate/components/DuplicateModal.tsx b/src/modules/duplicate/components/DuplicateModal.tsx index ea676bbc55..c952d26585 100644 --- a/src/modules/duplicate/components/DuplicateModal.tsx +++ b/src/modules/duplicate/components/DuplicateModal.tsx @@ -2,12 +2,6 @@ import React, { FC, useState } from 'react' import { useNavigate } from 'react-router-dom' import { useClient } from 'cozy-client' -import { - copy, - isDirectory, - moveRelateToSharedDrive -} from 'cozy-client/dist/models/file' -import { IOCozyFile } from 'cozy-client/types/types' import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert' import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n' @@ -17,6 +11,7 @@ import { File, FolderPickerEntry } from '@/components/FolderPicker/types' import { ROOT_DIR_ID } from '@/constants/config' import { useCancelable } from '@/modules/move/hooks/useCancelable' import { computeNextcloudFolderQueryId } from '@/modules/nextcloud/helpers' +import { buildCopyApi } from '@/modules/paste' interface DuplicateModalProps { entries: FolderPickerEntry[] @@ -25,7 +20,6 @@ interface DuplicateModalProps { showNextcloudFolder?: boolean isPublic?: boolean showSharedDriveFolder?: boolean - driveId?: string } const DuplicateModal: FC = ({ @@ -34,8 +28,7 @@ const DuplicateModal: FC = ({ onClose, showNextcloudFolder, isPublic, - showSharedDriveFolder, - driveId + showSharedDriveFolder }) => { const { t } = useI18n() const { showAlert } = useAlert() @@ -50,7 +43,9 @@ const DuplicateModal: FC = ({ setBusy(true) await Promise.all( entries.map(async entry => { - await registerCancelable(handleCopyApi(client, entry, folder)) + await registerCancelable( + buildCopyApi(client, entry, currentFolder, folder) + ) }) ) @@ -84,35 +79,6 @@ const DuplicateModal: FC = ({ } } - const handleCopyApi = async ( - client: unknown, - entry: FolderPickerEntry, - folder: File - ): Promise => { - if (driveId || entry.driveId || (folder as IOCozyFile).driveId) { - return await moveRelateToSharedDrive( - client, - { - instance: entry.driveId - ? currentFolder.attributes?.cozyMetadata?.createdOn - : '', - file_id: !isDirectory(entry) ? entry._id : '', - dir_id: isDirectory(entry) ? entry._id : '', - sharing_id: entry.driveId - }, - { - instance: (folder as IOCozyFile).driveId - ? folder.cozyMetadata?.createdOn - : '', - sharing_id: (folder as IOCozyFile).driveId, - dir_id: folder._id - }, - true - ) - } - return await copy(client, entry, folder) - } - /** * The content from nextcloud queries must be refreshed when coping files * This is only a proxy to Nextcloud queries so we don't have real-time or mutations updates diff --git a/src/modules/paste/index.js b/src/modules/paste/index.js index a70c76e55d..e6bf5bfc21 100644 --- a/src/modules/paste/index.js +++ b/src/modules/paste/index.js @@ -85,6 +85,36 @@ export const executeMove = async ( }) } +/** + * Executes a duplicate operation for files or folders. + * Automatically detects if it's a shared drive operation and uses the appropriate API. + * + * @param {CozyClient} client - The cozy client instance + * @param {import('@/components/FolderPicker/types').File} entry - The file or folder to move + * @param {import('@/components/FolderPicker/types').File} sourceDirectory - The source directory containing the entry + * @param {import('@/components/FolderPicker/types').File} destDirectory - The destination directory + * @param {boolean} [force=false] - Whether to force the move operation + * @returns {Promise} The result of the move operation + */ +export const executeDuplicate = async ( + client, + entry, + sourceDirectory, + destDirectory +) => { + const isSharedDriveOperation = entry.driveId || destDirectory.driveId + if (isSharedDriveOperation) { + return await executeSharedDriveMoveOrCopy( + client, + entry, + sourceDirectory, + destDirectory, + 'copy' + ) + } + return await copy(client, entry, destDirectory) +} + /** * Handles paste operations (copy or cut) for multiple files/folders. * Processes each file individually and handles validation, conflicts, and sharing permissions. @@ -137,6 +167,7 @@ export const handlePasteOperation = async ( const result = await handleDuplicateWithValidation( client, file, + sourceDirectory, targetFolder, { showAlert, t } ) @@ -184,12 +215,18 @@ export const handlePasteOperation = async ( const handleDuplicateWithValidation = async ( client, file, + sourceDirectory, targetFolder, options = {} ) => { const { showAlert, t } = options - const result = await copy(client, file, targetFolder) + const result = await executeDuplicate( + client, + file, + sourceDirectory, + targetFolder + ) const isCopyingInsideNextcloud = targetFolder._type === NEXTCLOUD_FILE_ID if (isCopyingInsideNextcloud) { diff --git a/src/modules/views/SharedDrive/SharedDriveFolderView.jsx b/src/modules/views/SharedDrive/SharedDriveFolderView.jsx index 1f29054aad..2558ce5a8c 100644 --- a/src/modules/views/SharedDrive/SharedDriveFolderView.jsx +++ b/src/modules/views/SharedDrive/SharedDriveFolderView.jsx @@ -49,7 +49,7 @@ const SharedDriveFolderView = () => { items: sharedDriveResult?.included || [], sharingContext, allowCut: canWriteToCurrentFolder, - allowCopy: false, + allowCopy: canWriteToCurrentFolder, pushModal, popModal, refresh From a915740b92601ae7830edfa7390f31295e054fd9 Mon Sep 17 00:00:00 2001 From: lethemanh Date: Thu, 20 Nov 2025 16:33:22 +0700 Subject: [PATCH 3/4] feat: Update type definition for paste module :sparkles: --- .../duplicate/components/DuplicateModal.tsx | 4 +- src/modules/paste/index.d.ts | 85 +++++++++++++++++++ .../views/Folder/SharedDriveDuplicateView.tsx | 1 - 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 src/modules/paste/index.d.ts diff --git a/src/modules/duplicate/components/DuplicateModal.tsx b/src/modules/duplicate/components/DuplicateModal.tsx index c952d26585..e1cfab0ca0 100644 --- a/src/modules/duplicate/components/DuplicateModal.tsx +++ b/src/modules/duplicate/components/DuplicateModal.tsx @@ -11,7 +11,7 @@ import { File, FolderPickerEntry } from '@/components/FolderPicker/types' import { ROOT_DIR_ID } from '@/constants/config' import { useCancelable } from '@/modules/move/hooks/useCancelable' import { computeNextcloudFolderQueryId } from '@/modules/nextcloud/helpers' -import { buildCopyApi } from '@/modules/paste' +import { executeDuplicate } from '@/modules/paste' interface DuplicateModalProps { entries: FolderPickerEntry[] @@ -44,7 +44,7 @@ const DuplicateModal: FC = ({ await Promise.all( entries.map(async entry => { await registerCancelable( - buildCopyApi(client, entry, currentFolder, folder) + executeDuplicate(client, entry as File, currentFolder, folder) ) }) ) diff --git a/src/modules/paste/index.d.ts b/src/modules/paste/index.d.ts new file mode 100644 index 0000000000..65406f9552 --- /dev/null +++ b/src/modules/paste/index.d.ts @@ -0,0 +1,85 @@ +import { CozyClient } from 'cozy-client' + +import { File } from '@/components/FolderPicker/types' + +export interface PasteOperationOptions { + showAlert?: (alert: { message: string; severity: string }) => void + t?: (key: string, params?: Record) => string + sharingContext?: { + getSharedParentPath?: (path: string) => string + hasSharedParent?: (path: string) => boolean + byDocId?: Record + } + showMoveValidationModal?: ( + modalType: string, + file: File, + targetFolder: File, + onConfirm: () => void, + onCancel: () => void + ) => void + isPublic?: boolean +} + +export interface PasteOperationResult { + success: boolean + file: File + error?: Error + operation: string +} + +/** + * Executes a move operation for files or folders. + * Automatically detects if it's a shared drive operation and uses the appropriate API. + * + * @param client - The cozy client instance + * @param entry - The file or folder to move + * @param sourceDirectory - The source directory containing the entry + * @param destDirectory - The destination directory + * @param force - Whether to force the move operation + * @returns The result of the move operation + */ +export function executeMove( + client: CozyClient, + entry: File, + sourceDirectory: File, + destDirectory: File, + force?: boolean +): Promise + +/** + * Executes a duplicate operation for files or folders. + * Automatically detects if it's a shared drive operation and uses the appropriate API. + * + * @param client - The cozy client instance + * @param entry - The file or folder to duplicate + * @param sourceDirectory - The source directory containing the entry + * @param destDirectory - The destination directory + * @returns The result of the duplicate operation + */ +export function executeDuplicate( + client: CozyClient, + entry: File, + sourceDirectory: File, + destDirectory: File +): Promise + +/** + * Handles paste operations (copy or cut) for multiple files/folders. + * Processes each file individually and handles validation, conflicts, and sharing permissions. + * + * @param client - The cozy client instance + * @param files - Array of files/folders to paste + * @param operation - The paste operation ('copy' or 'cut') + * @param sourceDirectory - The source directory containing the files + * @param targetFolder - The target folder for the paste operation + * @param options - Additional options + * @returns Array of operation results with success/failure status + */ +export function handlePasteOperation( + client: CozyClient, + files: File[], + operation: string | null, + sourceDirectory: File, + targetFolder: File, + options?: PasteOperationOptions +): Promise diff --git a/src/modules/views/Folder/SharedDriveDuplicateView.tsx b/src/modules/views/Folder/SharedDriveDuplicateView.tsx index ffe5c1a785..b5ea7936fc 100644 --- a/src/modules/views/Folder/SharedDriveDuplicateView.tsx +++ b/src/modules/views/Folder/SharedDriveDuplicateView.tsx @@ -38,7 +38,6 @@ const SharedDriveDuplicateView: FC = () => { currentFolder={displayedFolder} entries={sharedDriveResults} onClose={onClose} - driveId={displayedFolder.driveId} /> ) } From 85a3d40d8d3d12f34efad7e36512dbf475bbd7b2 Mon Sep 17 00:00:00 2001 From: lethemanh Date: Thu, 20 Nov 2025 16:43:54 +0700 Subject: [PATCH 4/4] fix: Resolve failed test cases :bug: --- src/hooks/useKeyboardShortcuts.spec.jsx | 9 ++++++--- src/modules/duplicate/components/DuplicateModal.tsx | 5 ++++- src/modules/paste/index.spec.js | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/hooks/useKeyboardShortcuts.spec.jsx b/src/hooks/useKeyboardShortcuts.spec.jsx index 0257b9cafb..1f2e9998cd 100644 --- a/src/hooks/useKeyboardShortcuts.spec.jsx +++ b/src/hooks/useKeyboardShortcuts.spec.jsx @@ -247,7 +247,8 @@ describe('useKeyboardShortcuts', () => { expect(mockCopyFiles).toHaveBeenCalledWith( mockSelectedItems, - new Set(['parent-folder-1', 'parent-folder-2']) + new Set(['parent-folder-1', 'parent-folder-2']), + mockCurrentFolder ) expect(mockShowAlert).toHaveBeenCalledWith({ message: 'alert.items_copied_2', @@ -311,7 +312,8 @@ describe('useKeyboardShortcuts', () => { expect(mockCopyFiles).toHaveBeenCalledWith( mockSelectedItems.filter(item => item.type === 'file'), - new Set(['parent-folder-1', 'parent-folder-2']) + new Set(['parent-folder-1', 'parent-folder-2']), + mockCurrentFolder ) }) }) @@ -761,7 +763,8 @@ describe('useKeyboardShortcuts', () => { expect(mockCopyFiles).toHaveBeenCalledWith( sharedDriveFiles, - new Set(['shared-folder-1']) + new Set(['shared-folder-1']), + sharedDriveFolder ) expect(mockShowAlert).toHaveBeenCalledWith({ message: 'alert.item_copied', diff --git a/src/modules/duplicate/components/DuplicateModal.tsx b/src/modules/duplicate/components/DuplicateModal.tsx index e1cfab0ca0..6a29fdd231 100644 --- a/src/modules/duplicate/components/DuplicateModal.tsx +++ b/src/modules/duplicate/components/DuplicateModal.tsx @@ -84,8 +84,11 @@ const DuplicateModal: FC = ({ * This is only a proxy to Nextcloud queries so we don't have real-time or mutations updates */ const refreshNextcloudQueries = (folder: File): void => { + const sourceAccount = folder.cozyMetadata?.sourceAccount + if (!sourceAccount || !folder.path) return + const queryId = computeNextcloudFolderQueryId({ - sourceAccount: folder.cozyMetadata?.sourceAccount, + sourceAccount, path: folder.path }) void client?.resetQuery(queryId) diff --git a/src/modules/paste/index.spec.js b/src/modules/paste/index.spec.js index 3b0e51994a..40ca941d65 100644 --- a/src/modules/paste/index.spec.js +++ b/src/modules/paste/index.spec.js @@ -360,7 +360,7 @@ describe('handlePasteOperation', () => { expect(result[1].success).toBe(false) expect(result[0].error).toBeInstanceOf(Error) expect(result[1].error).toBeInstanceOf(Error) - expect(copy).toHaveBeenCalledTimes(2) + expect(copy).not.toHaveBeenCalled() }) it('should handle missing options', async () => {