diff --git a/apps/chat-e2e/src/assertions/downloadAssertion.ts b/apps/chat-e2e/src/assertions/downloadAssertion.ts index 5221496d74..18801489f4 100644 --- a/apps/chat-e2e/src/assertions/downloadAssertion.ts +++ b/apps/chat-e2e/src/assertions/downloadAssertion.ts @@ -3,7 +3,20 @@ import { UploadDownloadData } from '@/src/ui/pages'; import { FileUtil } from '@/src/utils'; import { expect } from '@playwright/test'; +enum FileType { + JSON = 'json', + PLAIN = 'plain', + // JPG = 'jpg', +} +type FileReader = (path: string) => string | Buffer | object | undefined; + export class DownloadAssertion { + private static fileReaders: Record = { + [FileType.JSON]: FileUtil.readJsonFileData, + [FileType.PLAIN]: FileUtil.readPlainFileData, + // [FileType.JPG]: FileUtil.readJpgFileData, //class can be extended to use with different file types + }; + public async assertDownloadFileExtension( downloadedData: UploadDownloadData, expectedExtension: string, @@ -12,17 +25,27 @@ export class DownloadAssertion { expect(downloadedData.path).toMatch(new RegExp(`${expectedExtension}$`)); } - public async assertFileIsDownloaded(downloadedData: UploadDownloadData) { + public async assertFileIsDownloaded( + downloadedData: UploadDownloadData, + fileType: FileType, + ) { const downloadedFiles = FileUtil.getExportedFiles(); - expect - .soft( - downloadedFiles?.find( - (f) => - f.includes(downloadedData.path) && - FileUtil.readFileData(downloadedData.path) !== undefined, - ), - ExpectedMessages.dataIsExported, - ) - .toBeDefined(); + const fileExists = downloadedFiles?.some((file) => + file.includes(downloadedData.path), + ); + expect.soft(fileExists, ExpectedMessages.dataIsExported).toBeTruthy(); + if (fileExists) { + const fileReader = DownloadAssertion.fileReaders[fileType]; + const fileContent = fileReader(downloadedData.path); + expect.soft(fileContent, ExpectedMessages.dataIsExported).toBeDefined(); + } + } + + public async assertJsonFileIsDownloaded(downloadedData: UploadDownloadData) { + await this.assertFileIsDownloaded(downloadedData, FileType.JSON); + } + + public async assertPlainFileIsDownloaded(downloadedData: UploadDownloadData) { + await this.assertFileIsDownloaded(downloadedData, FileType.PLAIN); } } diff --git a/apps/chat-e2e/src/assertions/manageAttachmentsAssertion.ts b/apps/chat-e2e/src/assertions/manageAttachmentsAssertion.ts index 34885838e5..dfb55b06b2 100644 --- a/apps/chat-e2e/src/assertions/manageAttachmentsAssertion.ts +++ b/apps/chat-e2e/src/assertions/manageAttachmentsAssertion.ts @@ -1,5 +1,11 @@ -import { ElementState, ExpectedMessages, TreeEntity } from '@/src/testData'; -import { AttachFilesModal } from '@/src/ui/webElements'; +import { + ElementCaretState, + ElementState, + ExpectedMessages, + TreeEntity, +} from '@/src/testData'; +import { AttachFilesModal, FileModalSection } from '@/src/ui/webElements'; +import { AttachFilesTree } from '@/src/ui/webElements/entityTree'; import { expect } from '@playwright/test'; export class ManageAttachmentsAssertion { @@ -36,4 +42,47 @@ export class ManageAttachmentsAssertion { .soft(arrowIconColor[0], ExpectedMessages.sharedIconColorIsValid) .toBe(expectedColor); } + + public async assertEntityState( + entity: TreeEntity, + fileModalSection: FileModalSection, + expectedState: ElementState, + ) { + let entityTree: AttachFilesTree; + switch (fileModalSection) { + case FileModalSection.AllFiles: + entityTree = this.attachFilesModal.getAllFilesTree(); + break; + case FileModalSection.SharedWithMe: + entityTree = this.attachFilesModal.getSharedWithMeTree(); + break; + case FileModalSection.Organization: + entityTree = this.attachFilesModal.getOrganizationTree(); + break; + } + + const entityLocator = entityTree!.getEntityByName( + entity.name, + entity.index, + ); + expectedState === 'visible' + ? await expect + .soft(entityLocator, ExpectedMessages.entityIsVisible) + .toBeVisible() + : await expect + .soft(entityLocator, ExpectedMessages.entityIsNotVisible) + .toBeHidden(); + } + + public async assertSectionState( + section: FileModalSection, + state: ElementCaretState, + ) { + const sectionElement = this.attachFilesModal.getSectionElement(section); + const isExpanded = + await this.attachFilesModal.isSectionExpanded(sectionElement); + state === 'expanded' + ? expect(isExpanded, `Section "${section}" is ${state}`).toBeTruthy() + : expect(isExpanded, `Section "${section}" is ${state}`).toBeFalsy(); + } } diff --git a/apps/chat-e2e/src/core/dialFixtures.ts b/apps/chat-e2e/src/core/dialFixtures.ts index 26033ffada..2ea2d6d4ff 100644 --- a/apps/chat-e2e/src/core/dialFixtures.ts +++ b/apps/chat-e2e/src/core/dialFixtures.ts @@ -220,7 +220,7 @@ const dialTest = test.extend< selectFolderModal: SelectFolderModal; selectFolders: Folders; attachedAllFiles: Folders; - attachedFilesAssertion: ManageAttachmentsAssertion; + manageAttachmentsAssertion: ManageAttachmentsAssertion; settingsModal: SettingsModal; publishingRequestModal: PublishingRequestModal; conversationsToPublish: ConversationsToPublishTree; @@ -730,11 +730,11 @@ const dialTest = test.extend< const conversationAssertion = new ConversationAssertion(conversations); await use(conversationAssertion); }, - attachedFilesAssertion: async ({ attachFilesModal }, use) => { - const attachedFilesAssertion = new ManageAttachmentsAssertion( + manageAttachmentsAssertion: async ({ attachFilesModal }, use) => { + const manageAttachmentsAssertion = new ManageAttachmentsAssertion( attachFilesModal, ); - await use(attachedFilesAssertion); + await use(manageAttachmentsAssertion); }, organizationConversationAssertion: async ( { organizationConversations }, diff --git a/apps/chat-e2e/src/core/dialSharedWithMeFixtures.ts b/apps/chat-e2e/src/core/dialSharedWithMeFixtures.ts index 1a534cf3f5..7a5e7612f1 100644 --- a/apps/chat-e2e/src/core/dialSharedWithMeFixtures.ts +++ b/apps/chat-e2e/src/core/dialSharedWithMeFixtures.ts @@ -26,7 +26,9 @@ import config from '@/config/chat.playwright.config'; import { ChatAssertion, ConversationAssertion, + DownloadAssertion, ErrorToastAssertion, + ManageAttachmentsAssertion, } from '@/src/assertions'; import { ConfirmationDialogAssertion } from '@/src/assertions/confirmationDialogAssertion'; import { EntitySettingAssertion } from '@/src/assertions/entitySettingAssertion'; @@ -120,9 +122,24 @@ const dialSharedWithMeTest = dialTest.extend<{ additionalShareUserEntitySettingAssertion: EntitySettingAssertion; additionalShareUserAttachFilesModal: AttachFilesModal; additionalShareUserErrorToastAssertion: ErrorToastAssertion; + additionalShareUserManageAttachmentsAssertion: ManageAttachmentsAssertion; + additionalShareUserDownloadAssertion: DownloadAssertion; additionalShareUserChatAssertion: ChatAssertion; additionalShareUserConversationAssertion: ConversationAssertion; }>({ + // eslint-disable-next-line no-empty-pattern + additionalShareUserDownloadAssertion: async ({}, use) => { + const additionalShareUserDownloadAssertion = new DownloadAssertion(); + await use(additionalShareUserDownloadAssertion); + }, + additionalShareUserManageAttachmentsAssertion: async ( + { additionalShareUserAttachFilesModal }, + use, + ) => { + const additionalShareUserManageAttachmentsAssertion = + new ManageAttachmentsAssertion(additionalShareUserAttachFilesModal); + await use(additionalShareUserManageAttachmentsAssertion); + }, additionalShareUserErrorToastAssertion: async ( { additionalShareUserErrorToast }, use, diff --git a/apps/chat-e2e/src/tests/chatExportImport.test.ts b/apps/chat-e2e/src/tests/chatExportImport.test.ts index 287e8eb2f1..67884e7437 100644 --- a/apps/chat-e2e/src/tests/chatExportImport.test.ts +++ b/apps/chat-e2e/src/tests/chatExportImport.test.ts @@ -859,7 +859,7 @@ dialTest( 'Update id and name of exported conversations and import them again', async () => { for (const exportedData of exportedConversations) { - const exportedContent = FileUtil.readFileData(exportedData.path); + const exportedContent = FileUtil.readJsonFileData(exportedData.path); const conversation = exportedContent.history[0]; conversation.id = GeneratorUtil.randomString(10); conversation.name = GeneratorUtil.randomString(10); diff --git a/apps/chat-e2e/src/tests/importSpecific.test.ts b/apps/chat-e2e/src/tests/importSpecific.test.ts index b224e24fb7..f4402acbf4 100644 --- a/apps/chat-e2e/src/tests/importSpecific.test.ts +++ b/apps/chat-e2e/src/tests/importSpecific.test.ts @@ -84,7 +84,7 @@ dialTest( }); await dialTest.step('Update exported json', async () => { - const exportedData = FileUtil.readFileData(downloadedDataPath); + const exportedData = FileUtil.readJsonFileData(downloadedDataPath); exportedData.folders.map((f: FolderInterface) => { f.id = f.id.replace(folderToExport, updatedFolderName); f.name = f.name.replace(folderToExport, updatedFolderName); @@ -190,7 +190,7 @@ dialTest( }); await dialTest.step('Update exported json', async () => { - const exportedData = FileUtil.readFileData(downloadedDataPath); + const exportedData = FileUtil.readJsonFileData(downloadedDataPath); exportedData.folders.map((f: FolderInterface) => { f.id = f.id.replace(promptFolderToExport, updatedPromptFolderName); f.name = f.name.replace(promptFolderToExport, updatedPromptFolderName); diff --git a/apps/chat-e2e/src/tests/manageAttachment.test.ts b/apps/chat-e2e/src/tests/manageAttachment.test.ts index 2a2f217e73..89421aa709 100644 --- a/apps/chat-e2e/src/tests/manageAttachment.test.ts +++ b/apps/chat-e2e/src/tests/manageAttachment.test.ts @@ -53,7 +53,10 @@ dialTest( await dialTest.step( 'Hover over attached file, open file dropdown menu and select Delete option', async () => { - await attachFilesModal.openFileDropdownMenu(Attachment.sunImageName); + await attachFilesModal.openFileDropdownMenu( + Attachment.sunImageName, + FileModalSection.AllFiles, + ); await attachFilesModal .getFileDropdownMenu() .selectMenuOption(MenuOptions.delete); @@ -90,7 +93,10 @@ dialTest( await dialTest.step( 'Proceed again to "Confirm deleting file" modal, confirm file delete and verify it disappears from files list', async () => { - await attachFilesModal.openFileDropdownMenu(Attachment.sunImageName); + await attachFilesModal.openFileDropdownMenu( + Attachment.sunImageName, + FileModalSection.AllFiles, + ); await attachFilesModal .getFileDropdownMenu() .selectMenuOption(MenuOptions.delete); @@ -484,6 +490,7 @@ dialTest( async () => { await attachFilesModal.openFileDropdownMenu( ExpectedConstants.allowedSpecialSymbolsInName(), + FileModalSection.AllFiles, ); const downloadedData = await dialHomePage.downloadData(() => attachFilesModal diff --git a/apps/chat-e2e/src/tests/promptExportImport.test.ts b/apps/chat-e2e/src/tests/promptExportImport.test.ts index d29151d688..630c5910c8 100644 --- a/apps/chat-e2e/src/tests/promptExportImport.test.ts +++ b/apps/chat-e2e/src/tests/promptExportImport.test.ts @@ -765,7 +765,7 @@ dialTest( 'Update id and name of exported prompts and import them again', async () => { for (const exportedData of exportedPrompts) { - const exportedContent = FileUtil.readFileData(exportedData.path); + const exportedContent = FileUtil.readJsonFileData(exportedData.path); const prompt = exportedContent.prompts[0]; prompt.id = GeneratorUtil.randomString(10); prompt.name = GeneratorUtil.randomString(10); diff --git a/apps/chat-e2e/src/tests/shareConversationWithContent.test.ts b/apps/chat-e2e/src/tests/shareConversationWithContent.test.ts index c529ae02e0..58f618b6e5 100644 --- a/apps/chat-e2e/src/tests/shareConversationWithContent.test.ts +++ b/apps/chat-e2e/src/tests/shareConversationWithContent.test.ts @@ -508,7 +508,7 @@ dialTest( additionalUserShareApiHelper, shareApiAssertion, confirmationDialog, - attachedFilesAssertion, + manageAttachmentsAssertion, setTestIds, }) => { setTestIds('EPMRTC-3518', 'EPMRTC-3102'); @@ -563,11 +563,11 @@ dialTest( .getBottomDropdownMenu() .selectMenuOption(MenuOptions.attachments); - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( { name: Attachment.sunImageName }, 'visible', ); - await attachedFilesAssertion.assertEntityArrowIconColor( + await manageAttachmentsAssertion.assertEntityArrowIconColor( { name: Attachment.sunImageName }, Colors.controlsBackgroundAccent, ); @@ -579,11 +579,11 @@ dialTest( } await attachFilesModal.closeButton.hoverOver(); - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( { name: Attachment.cloudImageName }, 'visible', ); - await attachedFilesAssertion.assertEntityArrowIconColor( + await manageAttachmentsAssertion.assertEntityArrowIconColor( { name: Attachment.cloudImageName }, Colors.controlsBackgroundAccent, ); @@ -601,7 +601,7 @@ dialTest( .getFileDropdownMenu() .selectMenuOption(MenuOptions.unshare); await confirmationDialog.confirm({ triggeredHttpMethod: 'POST' }); - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( { name: Attachment.cloudImageName }, 'hidden', ); diff --git a/apps/chat-e2e/src/tests/sharedFilesAttachments.test.ts b/apps/chat-e2e/src/tests/sharedFilesAttachments.test.ts index e28aa6330b..a1bb05f337 100644 --- a/apps/chat-e2e/src/tests/sharedFilesAttachments.test.ts +++ b/apps/chat-e2e/src/tests/sharedFilesAttachments.test.ts @@ -8,6 +8,7 @@ import { AttachFilesFolders, Attachment, CollapsedSections, + ElementCaretState, ExpectedConstants, ExpectedMessages, MenuOptions, @@ -18,6 +19,7 @@ import { import { Colors } from '@/src/ui/domData'; import { FileModalSection } from '@/src/ui/webElements'; import { BucketUtil, GeneratorUtil, ModelsUtil } from '@/src/utils'; +import { expect } from '@playwright/test'; dialSharedWithMeTest( 'Arrow icon appears for file in Manage attachments if it was shared along with chat. The file is located in folders in "All files". The file is used in the model answer.\n' + @@ -27,7 +29,8 @@ dialSharedWithMeTest( 'Error message appears if to Share the conversation with an attachment from Shared with me\n' + 'Arrow icon stays for the file if the chat was unshared by the owner\n' + 'Arrow icon stays for the file if the chat was renamed or deleted, or model was changed\n' + - 'Arrow icon disappears if all the users delete the file from "Shared with me"', + 'Arrow icon disappears if all the users delete the file from "Shared with me"\n' + + 'Shared with me: the file with special chars in the name appears in "Shared with me" root', async ({ setTestIds, conversationData, @@ -36,7 +39,7 @@ dialSharedWithMeTest( mainUserShareApiHelper, additionalUserShareApiHelper, dialHomePage, - attachedFilesAssertion, + manageAttachmentsAssertion, chatBar, attachedAllFiles, localStorageManager, @@ -64,6 +67,7 @@ dialSharedWithMeTest( additionalSecondShareUserFileApiHelper, additionalShareUserFileApiHelper, errorToast, + additionalShareUserManageAttachmentsAssertion, }) => { dialSharedWithMeTest.slow(); setTestIds( @@ -71,6 +75,7 @@ dialSharedWithMeTest( 'EPMRTC-4134', /*'EPMRTC-4135,'*/ 'EPMRTC-4155', + 'EPMRTC-4156', 'EPMRTC-4123', 'EPMRTC-3116', 'EPMRTC-3122', @@ -239,11 +244,11 @@ dialSharedWithMeTest( await attachFilesModal.closeButton.hoverOver(); const firstImageEntity: TreeEntity = { name: Attachment.sunImageName }; - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( firstImageEntity, 'visible', ); - await attachedFilesAssertion.assertEntityArrowIconColor( + await manageAttachmentsAssertion.assertEntityArrowIconColor( firstImageEntity, Colors.controlsBackgroundAccent, ); @@ -251,11 +256,11 @@ dialSharedWithMeTest( const secondImageEntity: TreeEntity = { name: Attachment.cloudImageName, }; - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( secondImageEntity, 'visible', ); - await attachedFilesAssertion.assertEntityArrowIconColor( + await manageAttachmentsAssertion.assertEntityArrowIconColor( secondImageEntity, Colors.controlsBackgroundAccent, ); @@ -263,28 +268,28 @@ dialSharedWithMeTest( const thirdImageEntity: TreeEntity = { name: Attachment.flowerImageName, }; - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( thirdImageEntity, 'visible', ); - await attachedFilesAssertion.assertEntityArrowIconColor( + await manageAttachmentsAssertion.assertEntityArrowIconColor( thirdImageEntity, Colors.controlsBackgroundAccent, ); //TODO EPMRTC-4135 blocked by the #1076 // const fourthImageEntity: TreeEntity = { name: Attachment.heartImageName }; - // await attachedFilesAssertion.assertSharedFileArrowIconState(fourthImageEntity, 'visible'); - // await attachedFilesAssertion.assertEntityArrowIconColor(fourthImageEntity, Colors.controlsBackgroundAccent); + // await manageAttachmentsAssertion.assertSharedFileArrowIconState(fourthImageEntity, 'visible'); + // await manageAttachmentsAssertion.assertEntityArrowIconColor(fourthImageEntity, Colors.controlsBackgroundAccent); const specialCharsImageEntity: TreeEntity = { name: Attachment.specialSymbolsName, }; - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( specialCharsImageEntity, 'visible', ); - await attachedFilesAssertion.assertEntityArrowIconColor( + await manageAttachmentsAssertion.assertEntityArrowIconColor( specialCharsImageEntity, Colors.controlsBackgroundAccent, ); @@ -322,6 +327,12 @@ dialSharedWithMeTest( UploadMenuOptions.attachUploadedFiles, ); + await additionalShareUserManageAttachmentsAssertion.assertEntityState( + { name: Attachment.specialSymbolsName }, + FileModalSection.SharedWithMe, + 'visible', + ); + await additionalShareUserAttachFilesModal.checkAttachedFile( Attachment.specialSymbolsName, FileModalSection.SharedWithMe, @@ -405,11 +416,11 @@ dialSharedWithMeTest( await attachedAllFiles.expandFolder(AttachFilesFolders.images); await attachFilesModal.closeButton.hoverOver(); - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( { name: Attachment.sunImageName }, 'visible', ); - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( { name: Attachment.cloudImageName }, 'visible', ); @@ -444,7 +455,7 @@ dialSharedWithMeTest( await attachedAllFiles.expandFolder(specialCharsFolder); await attachFilesModal.closeButton.hoverOver(); - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( { name: Attachment.specialSymbolsName }, 'visible', ); @@ -476,7 +487,7 @@ dialSharedWithMeTest( await attachedAllFiles.expandFolder(specialCharsFolder); await attachFilesModal.closeButton.hoverOver(); - await attachedFilesAssertion.assertSharedFileArrowIconState( + await manageAttachmentsAssertion.assertSharedFileArrowIconState( { name: Attachment.specialSymbolsName }, 'hidden', ); @@ -485,3 +496,529 @@ dialSharedWithMeTest( ); }, ); + +dialSharedWithMeTest( + 'Shared with me: shared files located in "All folders" root appear in "Shared with me" root. The chat was shared.\n' + + 'Shared with me: shared files located in folders appear in "Shared with me" root. The chat was shared.\n' + + 'Shared with me: shared files appear in "Shared with me" root. The folder was shared.\n' + + 'Shared with me: download a file via context menu\n' + + 'Shared with me: delete a file via context menu\n' + + 'Shared with me: download multiple files\n' + + 'Shared with me: delete multiples files\n' + + "The 'Shared with me' section appears and disappears from Manage Attachments depending on the existence of shared files\n" + + 'Search: File from "Shared with me" is found\n' + + 'Search: No results found\n' + + 'Collapsed or expanded state of "Shared with me" is stored0', + async ({ + setTestIds, + conversationData, + dataInjector, + fileApiHelper, + mainUserShareApiHelper, + additionalUserShareApiHelper, + additionalShareUserSendMessage, + additionalShareUserConversations, + additionalShareUserSharedWithMeConversations, + additionalShareUserLocalStorageManager, + additionalShareUserChatMessages, + additionalShareUserAttachmentDropdownMenu, + additionalShareUserDialHomePage, + additionalShareUserDataInjector, + additionalShareUserManageAttachmentsAssertion, + additionalShareUserSharedFolderConversations, + additionalShareUserAttachFilesModal, + additionalShareUserDownloadAssertion, + additionalShareUserConfirmationDialog, + }) => { + setTestIds( + 'EPMRTC-3520', + 'EPMRTC-4129', + 'EPMRTC-4130', + 'EPMRTC-4149', + 'EPMRTC-4150', + 'EPMRTC-4151', + 'EPMRTC-4152', + 'EPMRTC-4153', + 'EPMRTC-4158', + 'EPMRTC-4159', + 'EPMRTC-4166', + ); + const user1ImageInRequest1 = Attachment.sunImageName; + const user1ImageInRequest2 = Attachment.cloudImageName; + const user1ImageInResponse1 = Attachment.heartImageName; + const user1ImageInResponse2 = Attachment.flowerImageName; + const user1ConversationInFolderImageInResponse1 = Attachment.longImageName; + + let user1ImageUrlInRequest1: string; + let user1ImageUrlInRequest2: string; + let user1ImageUrlInResponse1: string; + let user1ImageUrlInResponse2: string; + let user1ConversationInFolderImageUrlInResponse1: string; + + let shareByLinkResponse: ShareByLinkResponseModel; + let shareFolderByLinkResponse: ShareByLinkResponseModel; + + let conversationWithTwoRequestsWithAttachments: Conversation; + let conversationWithTwoResponsesWithAttachments: Conversation; + let secondUserEmptyConversation: Conversation; + const attachmentModel = GeneratorUtil.randomArrayElement( + ModelsUtil.getLatestModelsWithAttachment(), + ).id; + const user1FolderName = 'SharedFolder'; + let user1ConversationInFolder: Conversation; + + await dialTest.step( + 'User1 uploads an image to the "All files" root', + async () => { + user1ImageUrlInRequest1 = + await fileApiHelper.putFile(user1ImageInRequest1); + user1ImageUrlInRequest2 = + await fileApiHelper.putFile(user1ImageInRequest2); + + user1ImageUrlInResponse1 = await fileApiHelper.putFile( + user1ImageInResponse1, + ); + user1ImageUrlInResponse2 = await fileApiHelper.putFile( + user1ImageInResponse2, + ); + + user1ConversationInFolderImageUrlInResponse1 = + await fileApiHelper.putFile( + user1ConversationInFolderImageInResponse1, + ); + }, + ); + + await dialTest.step('User1 creates chats', async () => { + conversationWithTwoRequestsWithAttachments = + conversationData.prepareHistoryConversationWithAttachmentsInRequest({ + 1: { + model: attachmentModel, + attachmentUrl: [user1ImageUrlInRequest1], + }, + 2: { + model: attachmentModel, + attachmentUrl: [user1ImageUrlInRequest2], + }, + }); + conversationData.resetData(); + + conversationWithTwoResponsesWithAttachments = + conversationData.prepareHistoryConversationWithAttachmentsInResponse({ + 1: { + model: attachmentModel, + attachmentUrl: user1ImageUrlInResponse1, + }, + 2: { + model: attachmentModel, + attachmentUrl: user1ImageUrlInResponse2, + }, + }); + conversationData.resetData(); + + user1ConversationInFolder = + conversationData.prepareConversationWithAttachmentInResponse( + user1ConversationInFolderImageUrlInResponse1, + attachmentModel, + user1FolderName, + ); + conversationData.resetData(); + + await dataInjector.createConversations([ + conversationWithTwoRequestsWithAttachments, + conversationWithTwoResponsesWithAttachments, + user1ConversationInFolder, + ]); + }); + + await dialTest.step('User1 shares the chat with User2', async () => { + shareByLinkResponse = await mainUserShareApiHelper.shareEntityByLink([ + conversationWithTwoRequestsWithAttachments, + conversationWithTwoResponsesWithAttachments, + ]); + }); + + await dialTest.step( + 'User2 accepts share invitation by another user', + async () => { + await additionalUserShareApiHelper.acceptInvite(shareByLinkResponse); + await additionalShareUserLocalStorageManager.setSelectedConversation( + conversationWithTwoRequestsWithAttachments, + ); + }, + ); + + await dialTest.step('User1 shares the folder with User2', async () => { + shareFolderByLinkResponse = + await mainUserShareApiHelper.shareEntityByLink( + [user1ConversationInFolder], + true, + ); + }); + + await dialTest.step( + 'User2 accepts share invitation by another user', + async () => { + await additionalUserShareApiHelper.acceptInvite( + shareFolderByLinkResponse, + ); + }, + ); + + await dialTest.step( + 'User2 creates a chat with attachment modal accessible', + async () => { + secondUserEmptyConversation = + conversationData.prepareEmptyConversation(attachmentModel); + + conversationData.resetData(); + await additionalShareUserDataInjector.createConversations([ + secondUserEmptyConversation, + ]); + }, + ); + + await dialSharedWithMeTest.step( + 'User2 opens the file in the shared chat and verifies the picture is shown in requests', + async () => { + await additionalShareUserDialHomePage.openHomePage(); + await additionalShareUserDialHomePage.waitForPageLoaded(); + await additionalShareUserSharedWithMeConversations.selectConversation( + conversationWithTwoRequestsWithAttachments.name, + ); + + await additionalShareUserChatMessages.expandChatMessageAttachment( + 1, + user1ImageInRequest1, + ); + await additionalShareUserChatMessages.expandChatMessageAttachment( + 3, + user1ImageInRequest2, + ); + const attachmentUrl1 = + await additionalShareUserChatMessages.getChatMessageAttachmentUrl(1); + const attachmentUrl2 = + await additionalShareUserChatMessages.getChatMessageAttachmentUrl(3); + + expect(attachmentUrl1, ExpectedMessages.attachmentUrlIsValid).toContain( + `${API.importFileRootPath(BucketUtil.getBucket())}/${user1ImageInRequest1}`, + ); + expect(attachmentUrl2, ExpectedMessages.attachmentUrlIsValid).toContain( + `${API.importFileRootPath(BucketUtil.getBucket())}/${user1ImageInRequest2}`, + ); + }, + ); + + await dialSharedWithMeTest.step( + 'User2 opens the file in the shared chat and verifies the picture is shown in responses', + async () => { + await additionalShareUserSharedWithMeConversations.selectConversation( + conversationWithTwoResponsesWithAttachments.name, + ); + + await additionalShareUserChatMessages.expandChatMessageAttachment( + 2, + user1ImageInResponse1, + ); + await additionalShareUserChatMessages.expandChatMessageAttachment( + 4, + user1ImageInResponse2, + ); + const attachmentInResponseUrl1 = + await additionalShareUserChatMessages.getChatMessageAttachmentUrl(2); + const attachmentInResponseUrl2 = + await additionalShareUserChatMessages.getChatMessageAttachmentUrl(4); + + expect( + attachmentInResponseUrl1, + ExpectedMessages.attachmentUrlIsValid, + ).toContain( + `${API.importFileRootPath(BucketUtil.getBucket())}/${user1ImageInResponse1}`, + ); + expect( + attachmentInResponseUrl2, + ExpectedMessages.attachmentUrlIsValid, + ).toContain( + `${API.importFileRootPath(BucketUtil.getBucket())}/${user1ImageInResponse2}`, + ); + }, + ); + + await dialSharedWithMeTest.step( + 'User2 opens the file in the shared chat and verifies the picture is shown', + async () => { + //TODO expand folder + await additionalShareUserSharedFolderConversations.expandFolder( + user1FolderName, + ); + + await additionalShareUserSharedWithMeConversations.selectConversation( + user1ConversationInFolder.name, + ); + + await additionalShareUserChatMessages.expandChatMessageAttachment( + 2, + user1ConversationInFolderImageInResponse1, + ); + const attachmentUrl = + await additionalShareUserChatMessages.getChatMessageAttachmentUrl(2); + expect(attachmentUrl, ExpectedMessages.attachmentUrlIsValid).toContain( + `${API.importFileRootPath(BucketUtil.getBucket())}/${user1ConversationInFolderImageInResponse1}`, + ); + }, + ); + + await dialSharedWithMeTest.step( + 'User2 opens Manage attachments', + async () => { + await additionalShareUserConversations.selectConversation( + secondUserEmptyConversation.name, + ); + await additionalShareUserSendMessage.attachmentMenuTrigger.click(); + + await additionalShareUserAttachmentDropdownMenu.selectMenuOption( + UploadMenuOptions.attachUploadedFiles, + ); + }, + ); + + for (const section of [ + FileModalSection.AllFiles, + FileModalSection.SharedWithMe, + ]) { + for (const state of ['collapsed', 'expanded'] as ElementCaretState[]) { + await dialSharedWithMeTest.step( + `Collapsed or expanded state of "Shared with me" is stored. User2 sets the "${section}" section to ${state} state and reopens the modal`, + async () => { + await additionalShareUserAttachFilesModal.expandCollapseSection( + section, + ); // collapse or expand + + // Close and reopen the modal + await additionalShareUserAttachFilesModal.closeButton.click(); + await additionalShareUserSendMessage.attachmentMenuTrigger.click(); + await additionalShareUserSendMessage + .getDropdownMenu() + .selectMenuOption(UploadMenuOptions.attachUploadedFiles); + + await additionalShareUserManageAttachmentsAssertion.assertSectionState( + section, + state, + ); + }, + ); + } + } + + await dialSharedWithMeTest.step( + "The 'Shared with me' section appears with the existence of shared files", + async () => { + await additionalShareUserAttachFilesModal + .getSharedWithMeFilesContainer() + .waitForState({ state: 'visible' }); + }, + ); + + await dialSharedWithMeTest.step('User2 searches in files', async () => { + await additionalShareUserAttachFilesModal + .getSearchInput() + .fillInInput(user1ImageInRequest1.replace('.jpg', '')); + await additionalShareUserManageAttachmentsAssertion.assertEntityState( + { name: user1ImageInRequest1 }, + FileModalSection.SharedWithMe, + 'visible', + ); + await additionalShareUserManageAttachmentsAssertion.assertEntityState( + { name: user1ConversationInFolderImageInResponse1 }, + FileModalSection.SharedWithMe, + 'hidden', + ); + + await additionalShareUserAttachFilesModal + .getSearchInput() + .fillInInput(''); + }); + + await dialSharedWithMeTest.step('User2 searches in files', async () => { + await additionalShareUserAttachFilesModal + .getSearchInput() + .fillInInput(GeneratorUtil.randomString(10)); + await additionalShareUserAttachFilesModal + .getAllFilesTree() + .waitForState({ state: 'hidden' }); + await additionalShareUserAttachFilesModal + .getSharedWithMeTree() + .waitForState({ state: 'hidden' }); + + const allFiles = [ + user1ImageInRequest1, + user1ImageInRequest2, + user1ImageInResponse1, + user1ImageInResponse2, + user1ConversationInFolderImageInResponse1, + ]; + for (const file of allFiles) { + await additionalShareUserManageAttachmentsAssertion.assertEntityState( + { name: file }, + FileModalSection.SharedWithMe, + 'hidden', + ); + } + + await additionalShareUserAttachFilesModal + .getSearchInput() + .fillInInput(''); + await additionalShareUserAttachFilesModal + .getAllFilesTree() + .waitForState({ state: 'visible' }); + await additionalShareUserAttachFilesModal + .getSharedWithMeTree() + .waitForState({ state: 'visible' }); + }); + + await dialSharedWithMeTest.step('User2 observe shared files', async () => { + const allFiles = [ + user1ImageInRequest1, + user1ImageInRequest2, + user1ImageInResponse1, + user1ImageInResponse2, + user1ConversationInFolderImageInResponse1, + ]; + for (const file of allFiles) { + await additionalShareUserManageAttachmentsAssertion.assertEntityState( + { name: file }, + FileModalSection.SharedWithMe, + 'visible', + ); + } + }); + + await dialSharedWithMeTest.step( + 'User2 downloads a file via context menu', + async () => { + await additionalShareUserAttachFilesModal.openFileDropdownMenu( + user1ImageInRequest1, + FileModalSection.SharedWithMe, + ); + const downloadedData = + await additionalShareUserDialHomePage.downloadData(() => + additionalShareUserAttachFilesModal + .getFileDropdownMenu() + .selectMenuOption(MenuOptions.download), + ); + await additionalShareUserDownloadAssertion.assertPlainFileIsDownloaded( + downloadedData, + ); + }, + ); + + await dialSharedWithMeTest.step( + 'User2 downloads multiple files', + async () => { + const imagesToDownload = [ + user1ImageInRequest1, + user1ImageInRequest2, + user1ImageInResponse1, + user1ImageInResponse2, + user1ConversationInFolderImageInResponse1, + ]; + for (const file of imagesToDownload) { + await additionalShareUserAttachFilesModal.checkAttachedFile( + file, + FileModalSection.SharedWithMe, + ); + } + const downloadedData = + await additionalShareUserDialHomePage.downloadMultipleData( + () => + additionalShareUserAttachFilesModal.downloadFilesButton.click(), + imagesToDownload.length, + ); + for (const data of downloadedData) { + await additionalShareUserDownloadAssertion.assertPlainFileIsDownloaded( + data, + ); + } + for (const file of imagesToDownload) { + await additionalShareUserAttachFilesModal.checkAttachedFile( + file, + FileModalSection.SharedWithMe, + ); + } + }, + ); + + await dialSharedWithMeTest.step( + 'User2 deletes a file via context menu', + async () => { + await additionalShareUserAttachFilesModal.openFileDropdownMenu( + user1ImageInRequest1, + FileModalSection.SharedWithMe, + ); + await additionalShareUserAttachFilesModal + .getFileDropdownMenu() + .selectMenuOption(MenuOptions.delete); + await additionalShareUserConfirmationDialog.cancelDialog(); + await additionalShareUserManageAttachmentsAssertion.assertEntityState( + { name: user1ImageInRequest1 }, + FileModalSection.SharedWithMe, + 'visible', + ); + + await additionalShareUserAttachFilesModal.openFileDropdownMenu( + user1ImageInRequest1, + FileModalSection.SharedWithMe, + ); + await additionalShareUserAttachFilesModal + .getFileDropdownMenu() + .selectMenuOption(MenuOptions.delete); + await additionalShareUserConfirmationDialog.confirm({ + triggeredHttpMethod: 'POST', + }); + await additionalShareUserManageAttachmentsAssertion.assertEntityState( + { name: user1ImageInRequest1 }, + FileModalSection.SharedWithMe, + 'hidden', + ); + }, + ); + + await dialSharedWithMeTest.step( + 'User2 deletes multiple files', + async () => { + const imagesToDelete = [ + user1ImageInRequest2, + user1ImageInResponse1, + user1ImageInResponse2, + user1ConversationInFolderImageInResponse1, + ]; + for (const file of imagesToDelete) { + await additionalShareUserAttachFilesModal.checkAttachedFile( + file, + FileModalSection.SharedWithMe, + ); + } + await additionalShareUserAttachFilesModal.deleteFilesButton.click(); + await additionalShareUserConfirmationDialog.confirm({ + triggeredHttpMethod: 'POST', + }); + for (const file of imagesToDelete) { + await additionalShareUserManageAttachmentsAssertion.assertEntityState( + { name: file }, + FileModalSection.SharedWithMe, + 'hidden', + ); + } + }, + ); + + await dialSharedWithMeTest.step( + "The 'Shared with me' section disappears from Manage Attachments without shared files", + async () => { + await additionalShareUserAttachFilesModal + .getSharedWithMeFilesContainer() + .waitForState({ state: 'hidden' }); + }, + ); + }, +); diff --git a/apps/chat-e2e/src/tests/sharedPromptView.test.ts b/apps/chat-e2e/src/tests/sharedPromptView.test.ts index fab721b1aa..8d0b512774 100644 --- a/apps/chat-e2e/src/tests/sharedPromptView.test.ts +++ b/apps/chat-e2e/src/tests/sharedPromptView.test.ts @@ -95,7 +95,7 @@ dialSharedWithMeTest( exportedData, ExpectedConstants.exportedFileExtension, ); - await downloadAssertion.assertFileIsDownloaded(exportedData); + await downloadAssertion.assertJsonFileIsDownloaded(exportedData); }, ); diff --git a/apps/chat-e2e/src/ui/pages/basePage.ts b/apps/chat-e2e/src/ui/pages/basePage.ts index 3ccb273bd1..01b47728f0 100644 --- a/apps/chat-e2e/src/ui/pages/basePage.ts +++ b/apps/chat-e2e/src/ui/pages/basePage.ts @@ -3,7 +3,9 @@ import { keys } from '../keyboard'; import { API, Attachment, Import } from '@/src/testData'; import { Page } from '@playwright/test'; +import * as fs from 'node:fs'; import path from 'path'; +import { Download } from 'playwright-chromium'; export interface UploadDownloadData { path: string; @@ -169,30 +171,84 @@ export class BasePage { method: () => Promise, expectedDownloadsCount: number, filename?: string[] | string, - ) { + timeoutMs = 30000, + ): Promise { const downloadedData: UploadDownloadData[] = []; + const pendingDownloads = new Map< + string, + { download: Download; completed: boolean } + >(); let downloadCount = 0; - const receivedDownloads = new Promise((fulfill) => { - this.page.on('download', async (download) => { - const filenamePath = filename - ? typeof filename === 'string' - ? filename - : filename[downloadCount] - : download.suggestedFilename(); - const filePath = path.join(Import.exportPath, filenamePath); - downloadCount = downloadCount + 1; - downloadedData.push({ path: filePath, dataType: 'download' }); - await download.saveAs(filePath); - if (downloadCount === expectedDownloadsCount) { - fulfill(); + + const receivedDownloads = new Promise((fulfill, reject) => { + const timeoutId = setTimeout(() => { + cleanup(); + reject( + new Error( + `Timeout waiting for ${expectedDownloadsCount} downloads. Received ${downloadCount}`, + ), + ); + }, timeoutMs); + + const handleDownload = async (download: Download) => { + try { + const filenamePath = filename + ? typeof filename === 'string' + ? filename + : filename[downloadCount] + : download.suggestedFilename(); + + const filePath = path.join(Import.exportPath, filenamePath); + pendingDownloads.set(filenamePath, { download, completed: false }); + + await download.saveAs(filePath); + const fileExists = await fs.promises + .access(filePath) + .then(() => true) + .catch(() => false); + + if (!fileExists) { + throw new Error(`File ${filenamePath} failed to download`); + } + + downloadCount++; + pendingDownloads.get(filenamePath)!.completed = true; + downloadedData.push({ path: filePath, dataType: 'download' }); + + if (downloadCount === expectedDownloadsCount) { + clearTimeout(timeoutId); + cleanup(); + fulfill(); + } + } catch (error) { + clearTimeout(timeoutId); + cleanup(); + reject(error); } - }); + }; + + const cleanup = () => { + this.page.removeListener('download', handleDownload); + }; + + this.page.on('download', handleDownload); }); - await method(); - await receivedDownloads; - return downloadedData; - } + try { + await new Promise((resolve) => setTimeout(resolve, 100)); + await method(); + await receivedDownloads; + return downloadedData; + } catch (error) { + await Promise.all( + downloadedData.map((data) => + // eslint-disable-next-line @typescript-eslint/no-empty-function + fs.promises.unlink(data.path).catch(() => {}), + ), + ); + throw new Error(`Download failed:`); + } + } public async uploadData( uploadData: UploadDownloadData, method: () => Promise, diff --git a/apps/chat-e2e/src/ui/selectors/dialogSelectors.ts b/apps/chat-e2e/src/ui/selectors/dialogSelectors.ts index 76d5416f21..dfbcf5bca1 100644 --- a/apps/chat-e2e/src/ui/selectors/dialogSelectors.ts +++ b/apps/chat-e2e/src/ui/selectors/dialogSelectors.ts @@ -104,6 +104,8 @@ export const AttachFilesModalSelectors = { downloadFilesButton: '[data-qa="download-files"]', newFolderButton: '[data-qa="new-folder"]', arrowAdditionalIcon: '[data-qa="arrow-icon"]', + rootFolder: '[data-qa="section-root"]', + fileSection: '[data-qa="file-section-content"]', }; export const FilesModalSelectors = { diff --git a/apps/chat-e2e/src/ui/selectors/menuSelectors.ts b/apps/chat-e2e/src/ui/selectors/menuSelectors.ts index 847341b23e..c900e4462c 100644 --- a/apps/chat-e2e/src/ui/selectors/menuSelectors.ts +++ b/apps/chat-e2e/src/ui/selectors/menuSelectors.ts @@ -1,5 +1,5 @@ export const MenuSelectors = { menuTrigger: '[data-qa="menu-trigger"]', - dropdownMenu: '[data-qa="dropdown-menu"]', dotsMenu: '[aria-haspopup="menu"]', + dropdownMenu: '[data-qa="dropdown-menu"]', }; diff --git a/apps/chat-e2e/src/ui/selectors/sideBarSelectors.ts b/apps/chat-e2e/src/ui/selectors/sideBarSelectors.ts index 15ee6ac806..54f1333608 100644 --- a/apps/chat-e2e/src/ui/selectors/sideBarSelectors.ts +++ b/apps/chat-e2e/src/ui/selectors/sideBarSelectors.ts @@ -14,6 +14,7 @@ export const SideBarSelectors = { bottomPanel: '[data-qa="bottom-panel"]', arrowAdditionalIcon: '[data-qa="arrow-icon"]', search: '[data-qa="search"]', + searchInput: '[data-qa="search-input"]', folderSeparator: '.h-1', pinnedEntities: '[data-qa^="pinned"]', sharedWithMeContainer: '[data-qa="shared-with-me-container"]', diff --git a/apps/chat-e2e/src/ui/webElements/attachFilesModal.ts b/apps/chat-e2e/src/ui/webElements/attachFilesModal.ts index d4e6f82442..3ebe32ce87 100644 --- a/apps/chat-e2e/src/ui/webElements/attachFilesModal.ts +++ b/apps/chat-e2e/src/ui/webElements/attachFilesModal.ts @@ -11,11 +11,13 @@ import { import { DropdownMenu } from '@/src/ui/webElements/dropdownMenu'; import { AttachFilesTree, Folders } from '@/src/ui/webElements/entityTree'; import { FilesModalHeader } from '@/src/ui/webElements/filesModalHeader'; +import { Search } from '@/src/ui/webElements/search'; import { Page } from '@playwright/test'; export enum FileModalSection { AllFiles = 'All files', SharedWithMe = 'Shared with me', + Organization = 'Organization', } export class AttachFilesModal extends BaseElement { constructor(page: Page) { @@ -27,8 +29,16 @@ export class AttachFilesModal extends BaseElement { //'All files' section entities private allFolderFiles!: Folders; private allFilesTree!: AttachFilesTree; - private sharedWithMeTree!: AttachFilesTree; + private organizationTree!: AttachFilesTree; + private search!: Search; + + getSearchInput(): BaseElement { + if (!this.search) { + this.search = new Search(this.page, this.rootLocator); + } + return this.search; + } getFileDropdownMenu(): DropdownMenu { if (!this.fileDropdownMenu) { @@ -44,6 +54,31 @@ export class AttachFilesModal extends BaseElement { return this.modalHeader; } + public getSharedWithMeFilesContainer(): BaseElement { + return this.getChildElementBySelector( + AttachFilesModalSelectors.sharedWithMeFilesContainer, + ); + } + + public async expandCollapseSection(section: FileModalSection) { + let fileTree; + if (section === FileModalSection.AllFiles) { + fileTree = this.getAllFilesTree(); + } else if (section === FileModalSection.SharedWithMe) { + fileTree = this.getSharedWithMeTree(); + } else if (section === FileModalSection.Organization) { + fileTree = this.getOrganizationTree(); + } + await fileTree! + .getChildElementBySelector(AttachFilesModalSelectors.rootFolder) + .click(); + } + + public getAllFilesContainer(): BaseElement { + return this.getChildElementBySelector( + AttachFilesModalSelectors.allFilesContainer, + ); + } getAllFolderFiles(): Folders { if (!this.allFolderFiles) { this.allFolderFiles = new Folders( @@ -56,6 +91,17 @@ export class AttachFilesModal extends BaseElement { return this.allFolderFiles; } + getOrganizationTree(): AttachFilesTree { + if (!this.organizationTree) { + this.organizationTree = new AttachFilesTree( + this.page, + this.rootLocator, + AttachFilesModalSelectors.organizationFilesContainer, + ); + } + return this.organizationTree; + } + getAllFilesTree(): AttachFilesTree { if (!this.allFilesTree) { this.allFilesTree = new AttachFilesTree( @@ -67,6 +113,25 @@ export class AttachFilesModal extends BaseElement { return this.allFilesTree; } + public getSectionElement(section: FileModalSection): BaseElement { + switch (section) { + case FileModalSection.AllFiles: + return this.getChildElementBySelector( + AttachFilesModalSelectors.allFilesContainer, + ); + case FileModalSection.SharedWithMe: + return this.getChildElementBySelector( + AttachFilesModalSelectors.sharedWithMeFilesContainer, + ); + case FileModalSection.Organization: + return this.getChildElementBySelector( + AttachFilesModalSelectors.organizationFilesContainer, + ); + default: + throw new Error(`Unknown section: ${section}`); + } + } + getSharedWithMeTree(): AttachFilesTree { if (!this.sharedWithMeTree) { this.sharedWithMeTree = new AttachFilesTree( @@ -123,8 +188,17 @@ export class AttachFilesModal extends BaseElement { await this.waitForState({ state: 'hidden' }); } - public async openFileDropdownMenu(filename: string) { - const file = this.getAllFilesTree().getEntityByName(filename); + public async openFileDropdownMenu( + filename: string, + section: FileModalSection, + ) { + let fileTree; + if (section === FileModalSection.AllFiles) { + fileTree = this.getAllFilesTree(); + } else if (section === FileModalSection.SharedWithMe) { + fileTree = this.getSharedWithMeTree(); + } + const file = fileTree!.getEntityByName(filename); await file.hover(); await file.locator(MenuSelectors.dotsMenu).click(); await this.getFileDropdownMenu().waitForState(); @@ -135,4 +209,12 @@ export class AttachFilesModal extends BaseElement { ErrorLabelSelectors.errorText, ).getElementContent(); } + + public async isSectionExpanded( + sectionElement: BaseElement, + ): Promise { + return sectionElement + .getChildElementBySelector(AttachFilesModalSelectors.fileSection) + .isVisible(); + } } diff --git a/apps/chat-e2e/src/utils/fileUtil.ts b/apps/chat-e2e/src/utils/fileUtil.ts index 89a48f00d4..ad03e28dc5 100644 --- a/apps/chat-e2e/src/utils/fileUtil.ts +++ b/apps/chat-e2e/src/utils/fileUtil.ts @@ -16,11 +16,15 @@ export class FileUtil { return filename; } - public static readFileData(path: string) { + public static readJsonFileData(path: string) { const content = fs.readFileSync(path, 'utf-8'); return content.length > 0 ? JSON.parse(content) : undefined; } + public static readPlainFileData(path: string): Buffer | undefined { + return fs.readFileSync(path); + } + public static deleteExportFolder() { FileUtil.deleteFolder(Import.exportPath); } diff --git a/apps/chat/src/components/Files/FileManagerModal.tsx b/apps/chat/src/components/Files/FileManagerModal.tsx index 9f49494285..485c01d365 100644 --- a/apps/chat/src/components/Files/FileManagerModal.tsx +++ b/apps/chat/src/components/Files/FileManagerModal.tsx @@ -85,7 +85,10 @@ const FilesSectionWrapper = ({ className="!p-0" togglerClassName="ml-0.5" > -
+
{children}
@@ -664,6 +667,7 @@ export const FileManagerModal = ({ type="text" onChange={handleSearch} className="m-0 w-full rounded border border-primary bg-transparent px-3 py-2 outline-none placeholder:text-secondary focus-visible:border-accent-primary" + data-qa="search" >
{(isNothingExists || showNoResult) && ( diff --git a/apps/chat/src/components/Search/Search.tsx b/apps/chat/src/components/Search/Search.tsx index d2cd79313c..abdc45c7da 100644 --- a/apps/chat/src/components/Search/Search.tsx +++ b/apps/chat/src/components/Search/Search.tsx @@ -42,6 +42,7 @@ export default function Search({