From 8e58ca31626ed1938ffb551f5290d9e29489d028 Mon Sep 17 00:00:00 2001 From: irinakartun Date: Wed, 20 Dec 2023 20:50:56 +0300 Subject: [PATCH] e2e: added tests for Stop button behaviour (#311) Co-authored-by: Irina_Kartun Co-authored-by: Ilya Bondar Co-authored-by: Mikita Butsko --- e2e/src/core/localStorageManager .ts | 37 -- e2e/src/testData/expectedConstants.ts | 2 + e2e/src/testData/expectedMessages.ts | 3 + .../tests/chatBarFolderConversation.test.ts | 130 +++++-- e2e/src/tests/defaultModelSettings.test.ts | 122 ++++-- e2e/src/tests/workWithModels.test.ts | 348 +++++++++++++----- e2e/src/ui/domData/attributes.ts | 3 +- e2e/src/ui/domData/styles.ts | 1 + e2e/src/ui/webElements/dropdownMenu.ts | 2 +- e2e/src/ui/webElements/folders.ts | 8 +- 10 files changed, 452 insertions(+), 204 deletions(-) delete mode 100644 e2e/src/core/localStorageManager .ts diff --git a/e2e/src/core/localStorageManager .ts b/e2e/src/core/localStorageManager .ts deleted file mode 100644 index 3c7df57063..0000000000 --- a/e2e/src/core/localStorageManager .ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Conversation } from '@/src/types/chat'; -import { FolderInterface } from '@/src/types/folder'; -import { Prompt } from '@/src/types/prompt'; - -import { Page } from '@playwright/test'; - -export class LocalStorageManager { - private page: Page; - - constructor(page: Page) { - this.page = page; - } - - async setConversationHistory(...conversation: Conversation[]) { - await this.page.addInitScript((history) => { - window.localStorage.setItem('conversationHistory', history); - }, JSON.stringify(conversation)); - } - - async setSelectedConversation(conversation: Conversation) { - await this.page.addInitScript((selected) => { - window.localStorage.setItem('selectedConversationIds', selected); - }, `[${JSON.stringify(conversation.id)}]`); - } - - async setFolders(...folders: FolderInterface[]) { - await this.page.addInitScript((folders) => { - window.localStorage.setItem('folders', folders); - }, JSON.stringify(folders)); - } - - async setPrompts(...prompt: Prompt[]) { - await this.page.addInitScript((prompts) => { - window.localStorage.setItem('prompts', prompts); - }, JSON.stringify(prompt)); - } -} diff --git a/e2e/src/testData/expectedConstants.ts b/e2e/src/testData/expectedConstants.ts index ec1a5a6236..0ae8f53657 100644 --- a/e2e/src/testData/expectedConstants.ts +++ b/e2e/src/testData/expectedConstants.ts @@ -39,6 +39,8 @@ export const ExpectedConstants = { 'This mode replicates user requests from the original conversation including settings set in each message.', replayOldVersionWarning: 'Please note that some of your messages were created in older DIAL version. "Replay as is" could be working not as expected.', + regenerateResponseTooltip: + 'Please regenerate response to continue working with chat', }; export enum Groups { diff --git a/e2e/src/testData/expectedMessages.ts b/e2e/src/testData/expectedMessages.ts index 59548a6611..5dd8869943 100644 --- a/e2e/src/testData/expectedMessages.ts +++ b/e2e/src/testData/expectedMessages.ts @@ -15,6 +15,8 @@ export enum ExpectedMessages { newFolderCreated = 'New folder is created', folderCollapsed = 'Folder is collapsed', folderExpanded = 'Folder is expanded', + folderCaretIsVisible = 'Folder caret is visible', + folderCaretIsNotVisible = 'Folder caret is not visible', folderNameUpdated = 'Folder name is updated', folderNameNotUpdated = 'Folder name is not updated', folderDeleted = 'Folder is deleted', @@ -174,4 +176,5 @@ export enum ExpectedMessages { entityNameIsTruncated = 'Entity name is truncated', folderNameIsTruncated = 'Folder name is truncated in side panel', chatNameIsTruncated = 'Chat name is truncated in side panel', + sendButtonCursorIsNotAllowed = 'Send button cursor is not allowed', } diff --git a/e2e/src/tests/chatBarFolderConversation.test.ts b/e2e/src/tests/chatBarFolderConversation.test.ts index b1777c3d7d..dabb2885dc 100644 --- a/e2e/src/tests/chatBarFolderConversation.test.ts +++ b/e2e/src/tests/chatBarFolderConversation.test.ts @@ -5,9 +5,10 @@ import test from '@/e2e/src/core/fixtures'; import { ExpectedConstants, ExpectedMessages, + FolderConversation, MenuOptions, } from '@/e2e/src/testData'; -import { Overflow, Styles } from '@/e2e/src/ui/domData'; +import { Attributes, Overflow, Styles } from '@/e2e/src/ui/domData'; import { GeneratorUtil } from '@/e2e/src/utils'; import { expect } from '@playwright/test'; @@ -185,43 +186,100 @@ test( }, ); -test('Folders can expand and collapse', async ({ - dialHomePage, - conversationData, - folderConversations, - localStorageManager, - setTestIds, -}) => { - setTestIds('EPMRTC-579'); - const conversationInFolder = - conversationData.prepareDefaultConversationInFolder(); - await localStorageManager.setFolders(conversationInFolder.folders); - await localStorageManager.setConversationHistory( - conversationInFolder.conversations[0], - ); - const folderName = conversationInFolder.folders.name; +test( + 'Folders can expand and collapse.\n' + + 'Expanded and collapsed chat folders are stored locally.\n' + + '[UI] Expand/collapse icon near chat folder appears if there is any chat inside', + async ({ + dialHomePage, + conversationData, + folderConversations, + localStorageManager, + setTestIds, + }) => { + setTestIds('EPMRTC-579', 'EPMRTC-1315', 'EPMRTC-1365'); + let firstConversationInFolder: FolderConversation; + let secondConversationInFolder: FolderConversation; + let emptyFolder: FolderInterface; - await dialHomePage.openHomePage(); - await dialHomePage.waitForPageLoaded(); - await folderConversations.expandCollapseFolder(folderName); - let isConversationVisible = - await folderConversations.isFolderConversationVisible( - folderName, - conversationInFolder.conversations[0].name, - ); - expect - .soft(isConversationVisible, ExpectedMessages.folderExpanded) - .toBeTruthy(); + await test.step('Prepare 2 conversations inside folders and one empty folder', async () => { + firstConversationInFolder = + conversationData.prepareFolderWithConversations(1); + conversationData.resetData(); + secondConversationInFolder = + conversationData.prepareFolderWithConversations(1); + conversationData.resetData(); + emptyFolder = conversationData.prepareFolder(); + await localStorageManager.setFolders( + firstConversationInFolder.folders, + secondConversationInFolder.folders, + emptyFolder, + ); + await localStorageManager.setConversationHistory( + firstConversationInFolder.conversations[0], + secondConversationInFolder.conversations[0], + ); + }); + await test.step('Hover over folder with conversation and verify it has blue arrow', async () => { + await dialHomePage.openHomePage(); + await dialHomePage.waitForPageLoaded(); + await folderConversations + .getFolderByName(firstConversationInFolder.folders.name) + .hover(); + const firstFolderCaret = await folderConversations.getFolderCaret( + firstConversationInFolder.folders.name, + ); + expect + .soft(firstFolderCaret, ExpectedMessages.folderCaretIsVisible) + .toBe(Attributes.visible); - await folderConversations.expandCollapseFolder(folderName); - isConversationVisible = await folderConversations.isFolderConversationVisible( - folderName, - conversationInFolder.conversations[0].name, - ); - expect - .soft(isConversationVisible, ExpectedMessages.folderCollapsed) - .toBeFalsy(); -}); + await folderConversations.getFolderByName(emptyFolder.name).hover(); + const emptyFolderCaret = await folderConversations.getFolderCaret( + emptyFolder.name, + ); + expect + .soft(emptyFolderCaret, ExpectedMessages.folderCaretIsNotVisible) + .toBe(Attributes.invisible); + }); + + await test.step('Expand one folder, collapse another and verify state is saved after browser refresh', async () => { + await folderConversations.expandCollapseFolder( + firstConversationInFolder.folders.name, + ); + for (let i = 1; i <= 2; i++) { + await folderConversations.expandCollapseFolder( + secondConversationInFolder.folders.name, + ); + } + + let i = 2; + while (i > 0) { + if (i === 1) { + await dialHomePage.reloadPage(); + await dialHomePage.waitForPageLoaded(); + } + const isFirstConversationVisible = + await folderConversations.isFolderConversationVisible( + firstConversationInFolder.folders.name, + firstConversationInFolder.conversations[0].name, + ); + expect + .soft(isFirstConversationVisible, ExpectedMessages.folderExpanded) + .toBeTruthy(); + + const isSecondConversationVisible = + await folderConversations.isFolderConversationVisible( + secondConversationInFolder.folders.name, + secondConversationInFolder.conversations[0].name, + ); + expect + .soft(isSecondConversationVisible, ExpectedMessages.folderCollapsed) + .toBeFalsy(); + i--; + } + }); + }, +); test( 'Delete folder. Cancel.\n' + 'Delete root folder with nested folders', diff --git a/e2e/src/tests/defaultModelSettings.test.ts b/e2e/src/tests/defaultModelSettings.test.ts index 60b8e03fae..f7ad96dc79 100644 --- a/e2e/src/tests/defaultModelSettings.test.ts +++ b/e2e/src/tests/defaultModelSettings.test.ts @@ -2,8 +2,9 @@ import { OpenAIEntityModel } from '@/src/types/openai'; import test from '../core/fixtures'; import { ExpectedConstants, ExpectedMessages, ModelIds } from '../testData'; -import { Colors } from '../ui/domData'; +import { Colors, Cursors, Styles } from '../ui/domData'; +import { keys } from '@/e2e/src/ui/keyboard'; import { GeneratorUtil, ModelsUtil } from '@/e2e/src/utils'; import { expect } from '@playwright/test'; @@ -174,7 +175,8 @@ test( test( 'Default model in new chat is set as in previous chat.\n' + 'Send button is disabled if the message box is empty.\n' + - 'Chat name is shown in chat header', + 'Chat name is shown in chat header.\n' + + `It's impossible to send a message with spaces only`, async ({ dialHomePage, chatBar, @@ -184,48 +186,100 @@ test( sendMessage, chatHeader, tooltip, + chatMessages, + page, setTestIds, }) => { - setTestIds('EPMRTC-400', 'EPMRTC-474', 'EPMRTC-817'); + setTestIds('EPMRTC-400', 'EPMRTC-474', 'EPMRTC-817', 'EPMRTC-1568'); const request = 'test'; - await dialHomePage.openHomePage(); - await dialHomePage.waitForPageLoaded({ isNewConversationVisible: true }); - await talkToSelector.selectModel(bison.name); + await test.step('Verify Send button is disabled if no request message set and tooltip is shown on button hover', async () => { + await dialHomePage.openHomePage(); + await dialHomePage.waitForPageLoaded({ isNewConversationVisible: true }); + await talkToSelector.selectModel(bison.name); - const isSendMessageBtnEnabled = - await sendMessage.sendMessageButton.isElementEnabled(); - expect - .soft(isSendMessageBtnEnabled, ExpectedMessages.sendMessageButtonDisabled) - .toBeFalsy(); + const isSendMessageBtnEnabled = + await sendMessage.sendMessageButton.isElementEnabled(); + expect + .soft( + isSendMessageBtnEnabled, + ExpectedMessages.sendMessageButtonDisabled, + ) + .toBeFalsy(); - await sendMessage.sendMessageButton.hoverOver(); - const tooltipContent = await tooltip.getContent(); - expect - .soft(tooltipContent, ExpectedMessages.tooltipContentIsValid) - .toBe(ExpectedConstants.sendMessageTooltip); + await sendMessage.sendMessageButton.hoverOver(); + const tooltipContent = await tooltip.getContent(); + expect + .soft(tooltipContent, ExpectedMessages.tooltipContentIsValid) + .toBe(ExpectedConstants.sendMessageTooltip); + }); - await chat.sendRequestWithButton(request); - const chatTitle = await chatHeader.chatTitle.getElementInnerContent(); - expect - .soft(chatTitle, ExpectedMessages.headerTitleCorrespondRequest) - .toBe(request); + await test.step('Set spaces in the message input and Send button is disabled, tooltip is shown on hover, no message send on hit Enter', async () => { + for (let i = 1; i <= 2; i++) { + if (i === 2) { + const messagesCountBefore = + await chatMessages.chatMessages.getElementsCount(); + await sendMessage.messageInput.fillInInput(' '); + await page.keyboard.press(keys.enter); + const messagesCountAfter = + await chatMessages.chatMessages.getElementsCount(); + expect + .soft( + messagesCountBefore === messagesCountAfter, + ExpectedMessages.messageCountIsCorrect, + ) + .toBeTruthy(); + } + const isSendMessageBtnEnabled = + await sendMessage.sendMessageButton.isElementEnabled(); + expect + .soft( + isSendMessageBtnEnabled, + ExpectedMessages.sendMessageButtonDisabled, + ) + .toBeFalsy(); - await chatBar.createNewConversation(); - const modelBorderColors = await recentEntities - .getRecentEntity(bison.name) - .getAllBorderColors(); - Object.values(modelBorderColors).forEach((borders) => { - borders.forEach((borderColor) => { + await sendMessage.sendMessageButton.hoverOver(); + const sendBtnCursor = + await sendMessage.sendMessageButton.getComputedStyleProperty( + Styles.cursor, + ); expect - .soft(borderColor, ExpectedMessages.talkToEntityIsSelected) - .toBe(Colors.highlightedEntity); - }); + .soft(sendBtnCursor[0], ExpectedMessages.sendButtonCursorIsNotAllowed) + .toBe(Cursors.notAllowed); + + const tooltipContent = await tooltip.getContent(); + expect + .soft(tooltipContent, ExpectedMessages.tooltipContentIsValid) + .toBe(ExpectedConstants.sendMessageTooltip); + } }); - const recentTalkTo = await recentEntities.getRecentEntityNames(); - expect - .soft(recentTalkTo[0], ExpectedMessages.recentEntitiesIsOnTop) - .toBe(bison.name); + await test.step('Send new request and verify it is reflected in chat header', async () => { + await chat.sendRequestWithButton(request); + const chatTitle = await chatHeader.chatTitle.getElementInnerContent(); + expect + .soft(chatTitle, ExpectedMessages.headerTitleCorrespondRequest) + .toBe(request); + }); + + await test.step('Create new conversation and verify previous model is preselected and highlighted', async () => { + await chatBar.createNewConversation(); + const modelBorderColors = await recentEntities + .getRecentEntity(bison.name) + .getAllBorderColors(); + Object.values(modelBorderColors).forEach((borders) => { + borders.forEach((borderColor) => { + expect + .soft(borderColor, ExpectedMessages.talkToEntityIsSelected) + .toBe(Colors.highlightedEntity); + }); + }); + + const recentTalkTo = await recentEntities.getRecentEntityNames(); + expect + .soft(recentTalkTo[0], ExpectedMessages.recentEntitiesIsOnTop) + .toBe(bison.name); + }); }, ); diff --git a/e2e/src/tests/workWithModels.test.ts b/e2e/src/tests/workWithModels.test.ts index 28f3919183..45136d825f 100644 --- a/e2e/src/tests/workWithModels.test.ts +++ b/e2e/src/tests/workWithModels.test.ts @@ -1,5 +1,6 @@ import { Conversation } from '@/src/types/chat'; import { OpenAIEntityModel } from '@/src/types/openai'; +import { availableThemes } from '@/src/types/settings'; import test from '@/e2e/src/core/fixtures'; import { @@ -8,6 +9,8 @@ import { ExpectedMessages, ModelIds, } from '@/e2e/src/testData'; +import { Cursors, Styles } from '@/e2e/src/ui/domData'; +import { keys } from '@/e2e/src/ui/keyboard'; import { GeneratorUtil, ModelsUtil } from '@/e2e/src/utils'; import { expect } from '@playwright/test'; @@ -67,41 +70,125 @@ test('Regenerate response when answer was received', async ({ }); }); -test('Regenerate response when answer was not received', async ({ - dialHomePage, - chat, - setTestIds, - chatMessages, - context, -}) => { - setTestIds('EPMRTC-477'); - await test.step('Send a request in chat and emulate error until response received', async () => { - await dialHomePage.openHomePage(); - await dialHomePage.waitForPageLoaded({ isNewConversationVisible: true }); - await context.setOffline(true); - await chat.sendRequestWithButton('Type a fairytale', false); - }); - await test.step('Verify error is displayed as a response, regenerate button is available', async () => { - const generatedContent = await chatMessages.getLastMessageContent(); - expect - .soft(generatedContent, ExpectedMessages.errorReceivedOnReplay) - .toBe(ExpectedConstants.answerError); +test( + 'Regenerate response when answer was not received.\n' + + 'Model: Send action is unavailable if there is an error instead of response', + async ({ + dialHomePage, + chat, + setTestIds, + chatMessages, + context, + sendMessage, + tooltip, + localStorageManager, + page, + }) => { + setTestIds('EPMRTC-477', 'EPMRTC-1463'); + await test.step('Set random application theme', async () => { + const theme = GeneratorUtil.randomArrayElement( + Object.keys(availableThemes), + ); + await localStorageManager.setSettings(theme); + }); - const isGenerateResponseVisible = await chat.regenerate.isVisible(); - expect - .soft(isGenerateResponseVisible, ExpectedMessages.regenerateIsAvailable) - .toBeTruthy(); - }); - await test.step('Click Regenerate response and validate answer received', async () => { - await context.setOffline(false); - await chat.regenerateResponse(false); - await chatMessages.waitForPartialMessageReceived(2); - const generatedContent = await chatMessages.getLastMessageContent(); - expect - .soft(generatedContent, ExpectedMessages.messageContentIsValid) - .not.toContain(ExpectedConstants.answerError); - }); -}); + await test.step('Send a request in chat and emulate error until response received', async () => { + await dialHomePage.openHomePage(); + await dialHomePage.waitForPageLoaded({ isNewConversationVisible: true }); + await context.setOffline(true); + await chat.sendRequestWithButton('Type a fairytale', false); + }); + await test.step('Verify error is displayed as a response, regenerate button is available', async () => { + const generatedContent = await chatMessages.getLastMessageContent(); + expect + .soft(generatedContent, ExpectedMessages.errorReceivedOnReplay) + .toBe(ExpectedConstants.answerError); + + const isGenerateResponseVisible = await chat.regenerate.isVisible(); + expect + .soft(isGenerateResponseVisible, ExpectedMessages.regenerateIsAvailable) + .toBeTruthy(); + }); + + await test.step('Hover over Send button and verify it is disabled and tooltip is shown', async () => { + await context.setOffline(false); + for (let i = 1; i <= 2; i++) { + if (i === 2) { + const messagesCountBefore = + await chatMessages.chatMessages.getElementsCount(); + await sendMessage.messageInput.fillInInput( + GeneratorUtil.randomString(5), + ); + await page.keyboard.press(keys.enter); + const messagesCountAfter = + await chatMessages.chatMessages.getElementsCount(); + expect + .soft( + messagesCountBefore === messagesCountAfter, + ExpectedMessages.messageCountIsCorrect, + ) + .toBeTruthy(); + } + const isSendMessageBtnEnabled = + await sendMessage.sendMessageButton.isElementEnabled(); + expect + .soft( + isSendMessageBtnEnabled, + ExpectedMessages.sendMessageButtonDisabled, + ) + .toBeFalsy(); + + await sendMessage.sendMessageButton.hoverOver(); + const sendBtnCursor = + await sendMessage.sendMessageButton.getComputedStyleProperty( + Styles.cursor, + ); + expect + .soft(sendBtnCursor[0], ExpectedMessages.sendButtonCursorIsNotAllowed) + .toBe(Cursors.notAllowed); + + const tooltipContent = await tooltip.getContent(); + expect + .soft(tooltipContent, ExpectedMessages.tooltipContentIsValid) + .toBe(ExpectedConstants.regenerateResponseTooltip); + } + }); + + await test.step('Type any message, hit Enter key and verify Send button is disabled and tooltip is shown', async () => { + const isSendMessageBtnEnabled = + await sendMessage.sendMessageButton.isElementEnabled(); + expect + .soft( + isSendMessageBtnEnabled, + ExpectedMessages.sendMessageButtonDisabled, + ) + .toBeFalsy(); + + await sendMessage.sendMessageButton.hoverOver(); + const sendBtnCursor = + await sendMessage.sendMessageButton.getComputedStyleProperty( + Styles.cursor, + ); + expect + .soft(sendBtnCursor[0], ExpectedMessages.sendButtonCursorIsNotAllowed) + .toBe(Cursors.notAllowed); + + const tooltipContent = await tooltip.getContent(); + expect + .soft(tooltipContent, ExpectedMessages.tooltipContentIsValid) + .toBe(ExpectedConstants.regenerateResponseTooltip); + }); + + await test.step('Click Regenerate response and validate answer received', async () => { + await chat.regenerateResponse(false); + await chatMessages.waitForPartialMessageReceived(2); + const generatedContent = await chatMessages.getLastMessageContent(); + expect + .soft(generatedContent, ExpectedMessages.messageContentIsValid) + .not.toContain(ExpectedConstants.answerError); + }); + }, +); test( 'Edit the message in the middle. Cancel.\n' + @@ -259,72 +346,145 @@ test('System prompt is applied in Model', async ({ }); }); -test('Stop generating for models like GPT (1 symbol = 1 token)', async ({ - dialHomePage, - chat, - setTestIds, - chatMessages, -}) => { - setTestIds('EPMRTC-478'); - await test.step('Send request and stop generation immediately', async () => { - await dialHomePage.openHomePage(); - await dialHomePage.waitForPageLoaded({ isNewConversationVisible: true }); - await dialHomePage.throttleAPIResponse(API.chatHost); - await chat.sendRequestWithButton(request, false); - await chat.stopGenerating.click(); - }); +test( + 'Stop generating for models like GPT (1 symbol = 1 token).\n' + + 'Model: Send action is unavailable if there is empty response.\n' + + 'Edit the message after the response was stopped', + async ({ + dialHomePage, + chat, + setTestIds, + chatMessages, + sendMessage, + page, + tooltip, + localStorageManager, + }) => { + setTestIds('EPMRTC-478', 'EPMRTC-1480', 'EPMRTC-1309'); + await test.step('Set random application theme', async () => { + const theme = GeneratorUtil.randomArrayElement( + Object.keys(availableThemes), + ); + await localStorageManager.setSettings(theme); + }); - await test.step('Verify no content received and model icon is visible', async () => { - const receivedContent = await chatMessages.getLastMessageContent(); - expect - .soft(receivedContent, ExpectedMessages.messageContentIsValid) - .toBe(''); - const conversationIcon = await chatMessages.getIconAttributesForMessage(); - expect - .soft( - conversationIcon.iconEntity, - ExpectedMessages.chatBarIconEntityIsValid, - ) - .toBe(gpt35Model.id); - expect - .soft(conversationIcon.iconUrl, ExpectedMessages.chatBarIconSourceIsValid) - .toBe(gpt35Model.iconUrl); + await test.step('Send request and stop generation immediately', async () => { + await dialHomePage.openHomePage(); + await dialHomePage.waitForPageLoaded({ isNewConversationVisible: true }); + await dialHomePage.throttleAPIResponse(API.chatHost); + await chat.sendRequestWithButton(request, false); + await chat.stopGenerating.click(); + }); - const isRegenerateButtonVisible = await chat.regenerate.isVisible(); - expect - .soft(isRegenerateButtonVisible, ExpectedMessages.regenerateIsAvailable) - .toBeTruthy(); - }); + await test.step('Verify no content received and model icon is visible', async () => { + const receivedContent = await chatMessages.getLastMessageContent(); + expect + .soft(receivedContent, ExpectedMessages.messageContentIsValid) + .toBe(''); + const conversationIcon = await chatMessages.getIconAttributesForMessage(); + expect + .soft( + conversationIcon.iconEntity, + ExpectedMessages.chatBarIconEntityIsValid, + ) + .toBe(gpt35Model.id); + expect + .soft( + conversationIcon.iconUrl, + ExpectedMessages.chatBarIconSourceIsValid, + ) + .toBe(gpt35Model.iconUrl); + + const isRegenerateButtonVisible = await chat.regenerate.isVisible(); + expect + .soft(isRegenerateButtonVisible, ExpectedMessages.regenerateIsAvailable) + .toBeTruthy(); + }); + + await test.step('Hover over Send button and verify it is disabled and tooltip is shown', async () => { + for (let i = 1; i <= 2; i++) { + if (i === 2) { + const messagesCountBefore = + await chatMessages.chatMessages.getElementsCount(); + await sendMessage.messageInput.fillInInput(' '); + await page.keyboard.press(keys.enter); + const messagesCountAfter = + await chatMessages.chatMessages.getElementsCount(); + expect + .soft( + messagesCountBefore === messagesCountAfter, + ExpectedMessages.messageCountIsCorrect, + ) + .toBeTruthy(); + } + const isSendMessageBtnEnabled = + await sendMessage.sendMessageButton.isElementEnabled(); + expect + .soft( + isSendMessageBtnEnabled, + ExpectedMessages.sendMessageButtonDisabled, + ) + .toBeFalsy(); + + await sendMessage.sendMessageButton.hoverOver(); + const sendBtnCursor = + await sendMessage.sendMessageButton.getComputedStyleProperty( + Styles.cursor, + ); + expect + .soft(sendBtnCursor[0], ExpectedMessages.sendButtonCursorIsNotAllowed) + .toBe(Cursors.notAllowed); + + const tooltipContent = await tooltip.getContent(); + expect + .soft(tooltipContent, ExpectedMessages.tooltipContentIsValid) + .toBe(ExpectedConstants.regenerateResponseTooltip); + } + }); - await test.step('Send request and stop generation when partial content received', async () => { - await dialHomePage.unRouteResponse(API.chatHost); + await test.step('Send request and stop generation when partial content received', async () => { + await dialHomePage.unRouteResponse(API.chatHost); await chat.regenerateResponse(false); - await chatMessages.waitForPartialMessageReceived(2); - await chat.stopGenerating.click(); - }); + await chatMessages.waitForPartialMessageReceived(2); + await chat.stopGenerating.click(); + }); - await test.step('Verify partial content is preserved and model icon is visible', async () => { - const generatedContent = await chatMessages.getLastMessageContent(); - expect - .soft(generatedContent, ExpectedMessages.messageContentIsValid) - .not.toBe(''); - const conversationIcon = await chatMessages.getIconAttributesForMessage(); - expect - .soft( - conversationIcon.iconEntity, - ExpectedMessages.chatBarIconEntityIsValid, - ) - .toBe(gpt35Model.id); - expect - .soft(conversationIcon.iconUrl, ExpectedMessages.chatBarIconSourceIsValid) - .toBe(gpt35Model.iconUrl); + await test.step('Verify partial content is preserved and model icon is visible', async () => { + const generatedContent = await chatMessages.getLastMessageContent(); + expect + .soft(generatedContent, ExpectedMessages.messageContentIsValid) + .not.toBe(''); + const conversationIcon = await chatMessages.getIconAttributesForMessage(); + expect + .soft( + conversationIcon.iconEntity, + ExpectedMessages.chatBarIconEntityIsValid, + ) + .toBe(gpt35Model.id); + expect + .soft( + conversationIcon.iconUrl, + ExpectedMessages.chatBarIconSourceIsValid, + ) + .toBe(gpt35Model.iconUrl); - const isRegenerateButtonVisible = await chat.regenerate.isVisible(); - expect - .soft(isRegenerateButtonVisible, ExpectedMessages.regenerateIsAvailable) - .toBeTruthy(); - }); -}); + const isRegenerateButtonVisible = await chat.regenerate.isVisible(); + expect + .soft(isRegenerateButtonVisible, ExpectedMessages.regenerateIsAvailable) + .toBeTruthy(); + }); + + await test.step('Edit request, click "Save & Submit" and verify response is regenerated', async () => { + const updatedRequest = '1+2='; + await chatMessages.openEditMessageMode(request); + await chatMessages.editMessage(request, updatedRequest); + const messagesCount = await chatMessages.chatMessages.getElementsCount(); + expect + .soft(messagesCount, ExpectedMessages.messageCountIsCorrect) + .toBe(2); + }); + }, +); test( 'Send button in new message is available for Model if previous response is partly received when Stop generating was used.\n' + diff --git a/e2e/src/ui/domData/attributes.ts b/e2e/src/ui/domData/attributes.ts index da19315fa2..c06c0aa09b 100644 --- a/e2e/src/ui/domData/attributes.ts +++ b/e2e/src/ui/domData/attributes.ts @@ -1,6 +1,5 @@ export enum Attributes { value = 'value', - span = 'span', class = 'class', hidden = 'hidden', placeholder = 'placeholder', @@ -9,4 +8,6 @@ export enum Attributes { disabled = 'disabled', style = 'style', ariaLabel = 'aria-label', + visible = 'visible', + invisible = 'invisible', } diff --git a/e2e/src/ui/domData/styles.ts b/e2e/src/ui/domData/styles.ts index 0b9986df77..50a71b8d88 100644 --- a/e2e/src/ui/domData/styles.ts +++ b/e2e/src/ui/domData/styles.ts @@ -13,6 +13,7 @@ export enum Styles { export enum Cursors { pointer = 'pointer', + notAllowed = 'not-allowed', } export enum Overflow { diff --git a/e2e/src/ui/webElements/dropdownMenu.ts b/e2e/src/ui/webElements/dropdownMenu.ts index c3713f802d..758c265b4f 100644 --- a/e2e/src/ui/webElements/dropdownMenu.ts +++ b/e2e/src/ui/webElements/dropdownMenu.ts @@ -18,7 +18,7 @@ export class DropdownMenu extends BaseElement { public getMenuOption(option: string) { return this.createElementFromLocator( - this.menuOption(option).locator(Attributes.span), + this.menuOption(option).locator(Tags.span), ); } diff --git a/e2e/src/ui/webElements/folders.ts b/e2e/src/ui/webElements/folders.ts index 10c23b1aac..4e53ff3597 100644 --- a/e2e/src/ui/webElements/folders.ts +++ b/e2e/src/ui/webElements/folders.ts @@ -1,7 +1,7 @@ import { SideBarSelectors } from '../selectors'; import { BaseElement } from './baseElement'; -import { Styles } from '@/e2e/src/ui/domData'; +import { Attributes, Styles, Tags } from '@/e2e/src/ui/domData'; import { keys } from '@/e2e/src/ui/keyboard'; import { DropdownMenu } from '@/e2e/src/ui/webElements/dropdownMenu'; import { Input } from '@/e2e/src/ui/webElements/input'; @@ -49,6 +49,12 @@ export class Folders extends BaseElement { ); } + public getFolderCaret(name: string, index?: number) { + return this.getFolderByName(name, index) + .locator(Tags.span) + .getAttribute(Attributes.class); + } + public async getFoldersCount() { return this.getChildElementBySelector( SideBarSelectors.folder,