diff --git a/apps/chat-e2e/config/chat.playwright.config.ts b/apps/chat-e2e/config/chat.playwright.config.ts index e373f767f9..962ca0b31a 100644 --- a/apps/chat-e2e/config/chat.playwright.config.ts +++ b/apps/chat-e2e/config/chat.playwright.config.ts @@ -1,6 +1,8 @@ import { ResultFolder } from '@/src/testData'; import { defineConfig, devices } from '@playwright/test'; +export const overlayHost = 'http://localhost:4200'; + /** * See https://playwright.dev/docs/test-configuration. */ diff --git a/apps/chat-e2e/config/local.overlay.playwright.config.ts b/apps/chat-e2e/config/local.overlay.playwright.config.ts index 8bd3886faf..56c065e981 100644 --- a/apps/chat-e2e/config/local.overlay.playwright.config.ts +++ b/apps/chat-e2e/config/local.overlay.playwright.config.ts @@ -1,5 +1,6 @@ import config from './overlay.playwright.config'; +import { overlayHost } from '@/config/chat.playwright.config'; import { ResultFolder } from '@/src/testData'; import { workspaceRoot } from '@nx/devkit'; import { ReporterDescription } from '@playwright/test'; @@ -35,7 +36,7 @@ config.webServer = [ { cwd: workspaceRoot, command: 'npx nx serve:sandbox overlay-sandbox', - url: 'http://localhost:4200', + url: overlayHost, timeout: 180000, reuseExistingServer: true, env: { diff --git a/apps/chat-e2e/config/overlay.playwright.config.ts b/apps/chat-e2e/config/overlay.playwright.config.ts index bce7d6c01e..dbec95e332 100644 --- a/apps/chat-e2e/config/overlay.playwright.config.ts +++ b/apps/chat-e2e/config/overlay.playwright.config.ts @@ -1,4 +1,4 @@ -import config from './chat.playwright.config'; +import config, { overlayHost } from './chat.playwright.config'; import { ResultFolder } from '@/src/testData'; import { workspaceRoot } from '@nx/devkit'; @@ -21,14 +21,14 @@ config.reporter = [ }, ], ]; -config.use!.baseURL = 'http://localhost:4200'; +config.use!.baseURL = overlayHost; config.use!.navigationTimeout = 60000; config.use!.actionTimeout = 60000; config.webServer = { cwd: workspaceRoot, command: 'npx nx serve:sandbox:production overlay-sandbox', - url: 'http://localhost:4200', + url: overlayHost, timeout: 300000, reuseExistingServer: true, stdout: 'pipe', diff --git a/apps/chat-e2e/src/assertions/overlay/overlayAssertion.ts b/apps/chat-e2e/src/assertions/overlay/overlayAssertion.ts index c1be23e132..0129487a63 100644 --- a/apps/chat-e2e/src/assertions/overlay/overlayAssertion.ts +++ b/apps/chat-e2e/src/assertions/overlay/overlayAssertion.ts @@ -1,6 +1,7 @@ import { BaseAssertion } from '@/src/assertions'; -import { ExpectedMessages } from '@/src/testData'; +import { ExpectedMessages, Theme } from '@/src/testData'; import { Attributes } from '@/src/ui/domData'; +import { OverlayHomePage } from '@/src/ui/pages/overlay/overlayHomePage'; import { BaseElement } from '@/src/ui/webElements'; import { expect } from '@playwright/test'; @@ -42,4 +43,16 @@ export class OverlayAssertion extends BaseAssertion { throw new Error(propertyNotDefinedError); } } + + public async assertOverlayTheme( + overlayHomePage: OverlayHomePage, + expectedTheme: Theme, + ) { + expect + .soft( + await overlayHomePage.getTheme(), + ExpectedMessages.applicationThemeIsValid, + ) + .toBe(`${expectedTheme} ${expectedTheme}`); + } } diff --git a/apps/chat-e2e/src/core/baseFixtures.ts b/apps/chat-e2e/src/core/baseFixtures.ts index 0d119c12f1..3402e0b39f 100644 --- a/apps/chat-e2e/src/core/baseFixtures.ts +++ b/apps/chat-e2e/src/core/baseFixtures.ts @@ -1,6 +1,11 @@ import { BaseAssertion } from '@/src/assertions'; import { LocalStorageManager } from '@/src/core/localStorageManager'; -import { AuthProvider } from '@/src/testData'; +import { + AuthProvider, + ConversationData, + PromptData, + PublishRequestBuilder, +} from '@/src/testData'; import { Auth0Login } from '@/src/ui/actions/auth0Login'; import { AzureADLogin } from '@/src/ui/actions/azureADLogin'; import { KeycloakLogin } from '@/src/ui/actions/keycloakLogin'; @@ -43,6 +48,9 @@ const test = base.extend< baseAssertion: BaseAssertion; // eslint-disable-next-line @typescript-eslint/no-explicit-any incognitoProviderLogin: ProviderLogin; + conversationData: ConversationData; + promptData: PromptData; + publishRequestBuilder: PublishRequestBuilder; } >({ // eslint-disable-next-line no-empty-pattern @@ -181,6 +189,21 @@ const test = base.extend< } await use(incognitoProviderLogin); }, + // eslint-disable-next-line no-empty-pattern + conversationData: async ({}, use) => { + const conversationData = new ConversationData(); + await use(conversationData); + }, + // eslint-disable-next-line no-empty-pattern + promptData: async ({}, use) => { + const promptData = new PromptData(); + await use(promptData); + }, + // eslint-disable-next-line no-empty-pattern + publishRequestBuilder: async ({}, use) => { + const publishRequestBuilder = new PublishRequestBuilder(); + await use(publishRequestBuilder); + }, }); export default test; diff --git a/apps/chat-e2e/src/core/dialFixtures.ts b/apps/chat-e2e/src/core/dialFixtures.ts index 9864a3e792..e2595b91dc 100644 --- a/apps/chat-e2e/src/core/dialFixtures.ts +++ b/apps/chat-e2e/src/core/dialFixtures.ts @@ -61,7 +61,6 @@ import { SettingsModalAssertion } from '@/src/assertions/settingsModalAssertion' import { SideBarEntityAssertion } from '@/src/assertions/sideBarEntityAssertion'; import test from '@/src/core/baseFixtures'; import { isApiStorageType } from '@/src/hooks/global-setup'; -import { ConversationData, PublishRequestBuilder } from '@/src/testData'; import { ChatApiHelper, FileApiHelper, @@ -73,7 +72,6 @@ import { PublicationApiHelper } from '@/src/testData/api/publicationApiHelper'; import { ApiInjector } from '@/src/testData/injector/apiInjector'; import { BrowserStorageInjector } from '@/src/testData/injector/browserStorageInjector'; import { DataInjectorInterface } from '@/src/testData/injector/dataInjectorInterface'; -import { PromptData } from '@/src/testData/prompts/promptData'; import { AccountSettings } from '@/src/ui/webElements/accountSettings'; import { Addons } from '@/src/ui/webElements/addons'; import { AddonsDialog } from '@/src/ui/webElements/addonsDialog'; @@ -172,8 +170,6 @@ const dialTest = test.extend<{ addons: Addons; addonsDialog: AddonsDialog; agentInfo: AgentInfo; - conversationData: ConversationData; - promptData: PromptData; conversationDropdownMenu: DropdownMenu; folderDropdownMenu: DropdownMenu; promptDropdownMenu: DropdownMenu; @@ -187,7 +183,6 @@ const dialTest = test.extend<{ chatSettingsTooltip: ChatSettingsTooltip; compare: Compare; compareConversation: ConversationToCompare; - rightChatHeader: ChatHeader; leftChatHeader: ChatHeader; tooltip: Tooltip; @@ -233,7 +228,6 @@ const dialTest = test.extend<{ folderConversationsToPublish: FolderConversationsToPublish; publicationApiHelper: PublicationApiHelper; adminPublicationApiHelper: PublicationApiHelper; - publishRequestBuilder: PublishRequestBuilder; publishingRules: PublishingRules; conversationAssertion: ConversationAssertion; chatBarFolderAssertion: FolderAssertion; @@ -519,16 +513,6 @@ const dialTest = test.extend<{ const chatHeader = chat.getChatHeader(); await use(chatHeader); }, - // eslint-disable-next-line no-empty-pattern - conversationData: async ({}, use) => { - const conversationData = new ConversationData(); - await use(conversationData); - }, - // eslint-disable-next-line no-empty-pattern - promptData: async ({}, use) => { - const promptData = new PromptData(); - await use(promptData); - }, modelInfoTooltip: async ({ page }, use) => { const modelInfoTooltip = new ModelInfoTooltip(page); await use(modelInfoTooltip); @@ -744,11 +728,6 @@ const dialTest = test.extend<{ ); await use(adminPublicationApiHelper); }, - // eslint-disable-next-line no-empty-pattern - publishRequestBuilder: async ({}, use) => { - const publishRequestBuilder = new PublishRequestBuilder(); - await use(publishRequestBuilder); - }, publishingRules: async ({ publishingRequestModal }, use) => { const publishingRules = publishingRequestModal.getPublishingRules(); await use(publishingRules); diff --git a/apps/chat-e2e/src/core/dialOverlayFixtures.ts b/apps/chat-e2e/src/core/dialOverlayFixtures.ts index 726475c56e..d17d4c3928 100644 --- a/apps/chat-e2e/src/core/dialOverlayFixtures.ts +++ b/apps/chat-e2e/src/core/dialOverlayFixtures.ts @@ -1,34 +1,59 @@ import { + AccountSettings, AgentInfo, AgentSettings, + AttachFilesModal, Chat, ChatBar, ChatHeader, ChatMessages, + ConfirmationDialog, ConversationSettingsModal, - MarketplaceAgents, + DropdownMenu, + ModelInfoTooltip, + PromptBar, + PublishingRequestModal, + SendMessage, TalkToAgentDialog, + Toast, } from '../ui/webElements'; +import config from '@/config/overlay.playwright.config'; import { AgentInfoAssertion, - AgentSettingAssertion, ApiAssertion, BaseAssertion, - ChatHeaderAssertion, ChatMessagesAssertion, + ConversationAssertion, + PromptAssertion, TalkToAgentDialogAssertion, } from '@/src/assertions'; import { OverlayAssertion } from '@/src/assertions/overlay/overlayAssertion'; import test from '@/src/core/baseFixtures'; -import { IconApiHelper, ItemApiHelper } from '@/src/testData/api'; +import { + FileApiHelper, + IconApiHelper, + ItemApiHelper, + PublicationApiHelper, +} from '@/src/testData/api'; import { ApiInjector } from '@/src/testData/injector/apiInjector'; import { DataInjectorInterface } from '@/src/testData/injector/dataInjectorInterface'; import { OverlayHomePage } from '@/src/ui/pages/overlay/overlayHomePage'; import { OverlayMarketplacePage } from '@/src/ui/pages/overlay/overlayMarketplacePage'; -import { ConversationsTree } from '@/src/ui/webElements/entityTree'; +import { + ConversationsTree, + OrganizationConversationsTree, + PromptsTree, +} from '@/src/ui/webElements/entityTree'; +import { ReportAnIssueModal } from '@/src/ui/webElements/footer/reportAnIssueModal'; +import { RequestApiKeyModal } from '@/src/ui/webElements/footer/requestApiKeyModal'; import { Header } from '@/src/ui/webElements/header'; +import { ProfilePanel } from '@/src/ui/webElements/overlay/profilePanel'; +import { PlaybackControl } from '@/src/ui/webElements/playbackControl'; +import { SettingsModal } from '@/src/ui/webElements/settingsModal'; +import { ShareModal } from '@/src/ui/webElements/shareModal'; import path from 'path'; +import { APIRequestContext } from 'playwright-core'; import * as process from 'process'; export const overlayStateFilePath = (index: number) => @@ -42,35 +67,55 @@ const dialOverlayTest = test.extend<{ overlayAgentInfo: AgentInfo; overlayHeader: Header; overlayChatBar: ChatBar; + overlaySendMessage: SendMessage; overlayConversations: ConversationsTree; - chatHeader: ChatHeader; overlayChatHeader: ChatHeader; overlayChatMessages: ChatMessages; overlayConversationSettingsModal: ConversationSettingsModal; overlayAgentSettings: AgentSettings; overlayItemApiHelper: ItemApiHelper; + overlayPublicationApiHelper: PublicationApiHelper; + overlayFileApiHelper: FileApiHelper; overlayIconApiHelper: IconApiHelper; overlayApiInjector: ApiInjector; overlayDataInjector: DataInjectorInterface; overlayBaseAssertion: BaseAssertion; overlayAgentInfoAssertion: AgentInfoAssertion; - overlayChatHeaderAssertion: ChatHeaderAssertion; overlayChatMessagesAssertion: ChatMessagesAssertion; overlayApiAssertion: ApiAssertion; - overlayAgentSettingAssertion: AgentSettingAssertion; overlayTalkToAgentDialog: TalkToAgentDialog; - overlayTalkToAgents: MarketplaceAgents; - talkToAgentDialogAssertion: TalkToAgentDialogAssertion; + overlayPromptBar: PromptBar; + overlayPrompts: PromptsTree; + overlayConversationDropdownMenu: DropdownMenu; + overlayPromptDropdownMenu: DropdownMenu; + overlayShareModal: ShareModal; + overlayPublishingRequestModal: PublishingRequestModal; + overlayAccountSettings: AccountSettings; + overlayProfilePanel: ProfilePanel; + overlaySettingsModal: SettingsModal; + overlayConfirmationDialog: ConfirmationDialog; + overlayModelInfoTooltip: ModelInfoTooltip; + overlayToast: Toast; + overlayRequestApiKeyModal: RequestApiKeyModal; + overlayReportAnIssueModal: ReportAnIssueModal; + overlayAttachFilesModal: AttachFilesModal; + overlayPlaybackControl: PlaybackControl; + overlayOrganizationConversations: OrganizationConversationsTree; + overlayTalkToAgentDialogAssertion: TalkToAgentDialogAssertion; overlayAssertion: OverlayAssertion; + overlayConversationAssertion: ConversationAssertion; + overlayPromptAssertion: PromptAssertion; + adminUserRequestContext: APIRequestContext; + adminPublicationApiHelper: PublicationApiHelper; }>({ // eslint-disable-next-line no-empty-pattern storageState: async ({}, use) => { await use(overlayStateFilePath(+process.env.TEST_PARALLEL_INDEX!)); }, beforeTestCleanup: [ - async ({ overlayDataInjector }, use) => { - await overlayDataInjector.deleteAllData(true); - // await fileApiHelper.deleteAllFiles(); + async ({ overlayDataInjector, overlayFileApiHelper }, use) => { + await overlayDataInjector.deleteAllData(); + await overlayFileApiHelper.deleteAllFiles(); await use('beforeTestCleanup'); }, { scope: 'test', auto: true }, @@ -99,6 +144,10 @@ const dialOverlayTest = test.extend<{ const overlayChatBar = overlayHomePage.getOverlayContainer().getChatBar(); await use(overlayChatBar); }, + overlaySendMessage: async ({ overlayChat }, use) => { + const overlaySendMessage = overlayChat.getSendMessage(); + await use(overlaySendMessage); + }, overlayConversations: async ({ overlayChatBar }, use) => { const overlayConversations = overlayChatBar.getConversationsTree(); await use(overlayConversations); @@ -127,6 +176,14 @@ const dialOverlayTest = test.extend<{ const overlayItemApiHelper = new ItemApiHelper(request); await use(overlayItemApiHelper); }, + overlayPublicationApiHelper: async ({ request }, use) => { + const overlayPublicationApiHelper = new PublicationApiHelper(request); + await use(overlayPublicationApiHelper); + }, + overlayFileApiHelper: async ({ request }, use) => { + const overlayFileApiHelper = new FileApiHelper(request); + await use(overlayFileApiHelper); + }, overlayIconApiHelper: async ({ request }, use) => { const overlayIconApiHelper = new IconApiHelper(request); await use(overlayIconApiHelper); @@ -147,10 +204,6 @@ const dialOverlayTest = test.extend<{ const overlayAgentInfoAssertion = new AgentInfoAssertion(overlayAgentInfo); await use(overlayAgentInfoAssertion); }, - overlayChatHeaderAssertion: async ({ overlayChatHeader }, use) => { - const chatHeaderAssertion = new ChatHeaderAssertion(overlayChatHeader); - await use(chatHeaderAssertion); - }, overlayChatMessagesAssertion: async ({ overlayChatMessages }, use) => { const overlayChatMessagesAssertion = new ChatMessagesAssertion( overlayChatMessages, @@ -162,12 +215,6 @@ const dialOverlayTest = test.extend<{ const overlayApiAssertion = new ApiAssertion(); await use(overlayApiAssertion); }, - overlayAgentSettingAssertion: async ({ overlayAgentSettings }, use) => { - const overlayAgentSettingAssertion = new AgentSettingAssertion( - overlayAgentSettings, - ); - await use(overlayAgentSettingAssertion); - }, overlayTalkToAgentDialog: async ({ page, overlayHomePage }, use) => { const overlayTalkToAgentDialog = new TalkToAgentDialog( page, @@ -175,21 +222,149 @@ const dialOverlayTest = test.extend<{ ); await use(overlayTalkToAgentDialog); }, - overlayTalkToAgents: async ({ overlayTalkToAgentDialog }, use) => { - const talkToAgents = overlayTalkToAgentDialog.getAgents(); - await use(talkToAgents); + overlayPromptBar: async ({ overlayHomePage }, use) => { + const overlayPromptBar = overlayHomePage + .getOverlayContainer() + .getPromptBar(); + await use(overlayPromptBar); + }, + overlayPrompts: async ({ overlayPromptBar }, use) => { + const overlayPrompts = overlayPromptBar.getPromptsTree(); + await use(overlayPrompts); + }, + overlayConversationDropdownMenu: async ({ page, overlayHomePage }, use) => { + const overlayConversationDropdownMenu = new DropdownMenu( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayConversationDropdownMenu); + }, + overlayPromptDropdownMenu: async ({ page, overlayHomePage }, use) => { + const overlayPromptDropdownMenu = new DropdownMenu( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayPromptDropdownMenu); + }, + overlayShareModal: async ({ page, overlayHomePage }, use) => { + const overlayShareModal = new ShareModal( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayShareModal); + }, + overlayPublishingRequestModal: async ({ page, overlayHomePage }, use) => { + const publishingModal = new PublishingRequestModal( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(publishingModal); + }, + overlayAccountSettings: async ({ overlayHeader }, use) => { + const overlayAccountSettings = overlayHeader.getAccountSettings(); + await use(overlayAccountSettings); + }, + overlayProfilePanel: async ({ page, overlayHomePage }, use) => { + const overlayProfilePanel = new ProfilePanel( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayProfilePanel); + }, + overlaySettingsModal: async ({ page, overlayHomePage }, use) => { + const overlaySettingsModal = new SettingsModal( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlaySettingsModal); + }, + overlayConfirmationDialog: async ({ page, overlayHomePage }, use) => { + const overlayConfirmationDialog = new ConfirmationDialog( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayConfirmationDialog); + }, + overlayModelInfoTooltip: async ({ page, overlayHomePage }, use) => { + const overlayModelInfoTooltip = new ModelInfoTooltip( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayModelInfoTooltip); + }, + overlayToast: async ({ page, overlayHomePage }, use) => { + const overlayToast = new Toast( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayToast); + }, + overlayRequestApiKeyModal: async ({ page, overlayHomePage }, use) => { + const overlayRequestApiKeyModal = new RequestApiKeyModal( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayRequestApiKeyModal); }, - talkToAgentDialogAssertion: async ({ overlayTalkToAgentDialog }, use) => { - const talkToAgentDialogAssertion = new TalkToAgentDialogAssertion( + overlayReportAnIssueModal: async ({ page, overlayHomePage }, use) => { + const overlayReportAnIssueModal = new ReportAnIssueModal( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayReportAnIssueModal); + }, + overlayAttachFilesModal: async ({ page, overlayHomePage }, use) => { + const overlayAttachFilesModal = new AttachFilesModal( + page, + overlayHomePage.getOverlayContainer().getElementLocator(), + ); + await use(overlayAttachFilesModal); + }, + overlayPlaybackControl: async ({ overlayChat }, use) => { + const overlayPlaybackControl = overlayChat.getPlaybackControl(); + await use(overlayPlaybackControl); + }, + overlayOrganizationConversations: async ({ overlayChatBar }, use) => { + const overlayOrganizationConversations = + overlayChatBar.getOrganizationConversationsTree(); + await use(overlayOrganizationConversations); + }, + overlayTalkToAgentDialogAssertion: async ( + { overlayTalkToAgentDialog }, + use, + ) => { + const overlayTalkToAgentDialogAssertion = new TalkToAgentDialogAssertion( overlayTalkToAgentDialog, ); - await use(talkToAgentDialogAssertion); + await use(overlayTalkToAgentDialogAssertion); }, // eslint-disable-next-line no-empty-pattern overlayAssertion: async ({}, use) => { const overlayAssertion = new OverlayAssertion(); await use(overlayAssertion); }, + overlayConversationAssertion: async ({ overlayConversations }, use) => { + const overlayConversationAssertion = new ConversationAssertion( + overlayConversations, + ); + await use(overlayConversationAssertion); + }, + overlayPromptAssertion: async ({ overlayPrompts }, use) => { + const promptAssertion = new PromptAssertion(overlayPrompts); + await use(promptAssertion); + }, + adminUserRequestContext: async ({ playwright }, use) => { + const adminUserRequestContext = await playwright.request.newContext({ + storageState: overlayStateFilePath(+config.workers!), + }); + await use(adminUserRequestContext); + }, + adminPublicationApiHelper: async ({ adminUserRequestContext }, use) => { + const adminPublicationApiHelper = new PublicationApiHelper( + adminUserRequestContext, + ); + await use(adminPublicationApiHelper); + }, }); export default dialOverlayTest; diff --git a/apps/chat-e2e/src/core/localStorageManager.ts b/apps/chat-e2e/src/core/localStorageManager.ts index e1e524f08b..a8b10cf99c 100644 --- a/apps/chat-e2e/src/core/localStorageManager.ts +++ b/apps/chat-e2e/src/core/localStorageManager.ts @@ -168,4 +168,21 @@ export class LocalStorageManager { async setChatbarWidth(width: string) { await this.page.addInitScript(this.setChatbarWidthKey(), width); } + + async getSelectedConversationIds(originHost?: string) { + let selectedConversationIds; + const storage = await this.page.context().storageState(); + let origin; + if (originHost) { + origin = storage.origins.find((o) => o.origin === originHost); + } else { + origin = storage.origins[0]; + } + if (origin) { + selectedConversationIds = origin.localStorage.find( + (s) => s.name === 'selectedConversationIds', + )?.value; + } + return selectedConversationIds ? JSON.parse(selectedConversationIds) : ''; + } } diff --git a/apps/chat-e2e/src/testData/api/baseApiHelper.ts b/apps/chat-e2e/src/testData/api/baseApiHelper.ts index 7f4cc1658a..cec9bb9ff3 100644 --- a/apps/chat-e2e/src/testData/api/baseApiHelper.ts +++ b/apps/chat-e2e/src/testData/api/baseApiHelper.ts @@ -1,3 +1,4 @@ +import config, { overlayHost } from '@/config/chat.playwright.config'; import { APIRequestContext } from '@playwright/test'; export class BaseApiHelper { @@ -6,4 +7,13 @@ export class BaseApiHelper { constructor(request: APIRequestContext) { this.request = request; } + + //function to override the API host if overlay sandbox is running + public getHost(endpoint: string) { + const baseUrl = config.use!.baseURL; + if (baseUrl === overlayHost) { + endpoint = process.env.NEXT_PUBLIC_OVERLAY_HOST + endpoint; + } + return endpoint; + } } diff --git a/apps/chat-e2e/src/testData/api/fileApiHelper.ts b/apps/chat-e2e/src/testData/api/fileApiHelper.ts index ab2432b30a..d1b7e6b0cb 100644 --- a/apps/chat-e2e/src/testData/api/fileApiHelper.ts +++ b/apps/chat-e2e/src/testData/api/fileApiHelper.ts @@ -31,7 +31,7 @@ export class FileApiHelper extends BaseApiHelper { ? `${baseUrl}/${encodedParentPath}/${encodedFilename}` : `${baseUrl}/${encodedFilename}`; - const response = await this.request.put(url, { + const response = await this.request.put(this.getHost(url), { headers: { Accept: '*/*', ContentType: 'multipart/form-data', @@ -72,14 +72,14 @@ export class FileApiHelper extends BaseApiHelper { public async getFile(filePath: string) { const baseUrl = `${API.fileHost}/${this.userBucket ?? BucketUtil.getBucket()}`; const url = `${baseUrl}/${filePath}`; - const response = await this.request.get(url); + const response = await this.request.get(this.getHost(url)); expect(response.status()).toBe(200); return response; } public async deleteFromAllFiles(path: string) { const url = `/api/${path}`; - const response = await this.request.delete(url); + const response = await this.request.delete(this.getHost(url)); expect(response.status(), `File by path: ${path} was deleted`).toBe(200); } @@ -89,7 +89,7 @@ export class FileApiHelper extends BaseApiHelper { const requestData = { resources: [{ url: encodedPath }], }; - const response = await this.request.post(url, { + const response = await this.request.post(this.getHost(url), { data: requestData, }); expect( @@ -102,7 +102,7 @@ export class FileApiHelper extends BaseApiHelper { const host = url ? `${API.listingHost}/${url.substring(0, url.length - 1)}` : `${API.filesListingHost()}/${this.userBucket ?? BucketUtil.getBucket()}`; - const response = await this.request.get(host, { + const response = await this.request.get(this.getHost(host), { params: { filter: nodeType, }, diff --git a/apps/chat-e2e/src/testData/api/itemApiHelper.ts b/apps/chat-e2e/src/testData/api/itemApiHelper.ts index 3d676bf8e9..22cd86e18a 100644 --- a/apps/chat-e2e/src/testData/api/itemApiHelper.ts +++ b/apps/chat-e2e/src/testData/api/itemApiHelper.ts @@ -16,43 +16,32 @@ export class ItemApiHelper extends BaseApiHelper { this.userBucket = userBucket; } - public async deleteAllData(bucket?: string, isOverlay = false) { + public async deleteAllData(bucket?: string) { const bucketToUse = this.userBucket ?? bucket; const conversations = await this.listItems( API.conversationsHost(), bucketToUse, - isOverlay, ); - const prompts = await this.listItems( - API.promptsHost(), - bucketToUse, - isOverlay, - ); - await this.deleteBackendItem(isOverlay, ...conversations, ...prompts); + const prompts = await this.listItems(API.promptsHost(), bucketToUse); + await this.deleteBackendItem(...conversations, ...prompts); } - public async listItems(url: string, bucket?: string, isOverlay?: boolean) { + public async listItems(url: string, bucket?: string) { const bucketToUse = this.userBucket ?? bucket; - return this.getItems( - `${url}/${bucketToUse ?? BucketUtil.getBucket()}`, - isOverlay, - ); + return this.getItems(`${url}/${bucketToUse ?? BucketUtil.getBucket()}`); } public async listItem(itemUrl: string) { return this.getItems(`${API.listingHost}/${itemUrl}`); } - public async getItems(url: string, isOverlay?: boolean) { - const response = await this.request.get( - isOverlay ? process.env.NEXT_PUBLIC_OVERLAY_HOST + url : url, - { - params: { - filter: BackendDataNodeType.ITEM, - recursive: true, - }, + public async getItems(url: string) { + const response = await this.request.get(this.getHost(url), { + params: { + filter: BackendDataNodeType.ITEM, + recursive: true, }, - ); + }); const statusCode = response.status(); expect( statusCode, @@ -61,16 +50,10 @@ export class ItemApiHelper extends BaseApiHelper { return (await response.json()) as BackendDataEntity[]; } - public async deleteBackendItem( - isOverlay?: boolean, - ...items: BackendDataEntity[] - ) { + public async deleteBackendItem(...items: BackendDataEntity[]) { for (const item of items) { const path = `/api/${item.url}`; - const url = isOverlay - ? process.env.NEXT_PUBLIC_OVERLAY_HOST + path - : path; - const response = await this.request.delete(url); + const response = await this.request.delete(this.getHost(path)); expect( response.status(), `Backend item with id: ${item.name} was successfully deleted`, @@ -80,7 +63,7 @@ export class ItemApiHelper extends BaseApiHelper { public async deleteEntity(entity: Entity) { const url = `/api/${entity.id}`; - const response = await this.request.delete(url); + const response = await this.request.delete(this.getHost(url)); expect( response.status(), `Entity with id: ${entity.name} was successfully deleted`, @@ -115,7 +98,7 @@ export class ItemApiHelper extends BaseApiHelper { public async createItem(item: Prompt | Conversation) { const url = `/api/${ItemUtil.getEncodedItemId(item.id)}`; - const response = await this.request.put(url, { + const response = await this.request.put(this.getHost(url), { data: item, }); expect( diff --git a/apps/chat-e2e/src/testData/api/publicationApiHelper.ts b/apps/chat-e2e/src/testData/api/publicationApiHelper.ts index 0cff397e4a..9145514a4b 100644 --- a/apps/chat-e2e/src/testData/api/publicationApiHelper.ts +++ b/apps/chat-e2e/src/testData/api/publicationApiHelper.ts @@ -15,9 +15,14 @@ export type PublicationProps = PublicationInfo & Partial; export class PublicationApiHelper extends BaseApiHelper { public async listPublicationRequests() { - const response = await this.request.post(API.pendingPublicationsListing, { - data: { url: `publications/${ExpectedConstants.rootPublicationFolder}` }, - }); + const response = await this.request.post( + this.getHost(API.pendingPublicationsListing), + { + data: { + url: `publications/${ExpectedConstants.rootPublicationFolder}`, + }, + }, + ); const statusCode = response.status(); expect( statusCode, @@ -27,9 +32,12 @@ export class PublicationApiHelper extends BaseApiHelper { } public async getPublicationRequestDetails(publicationUrl: string) { - const response = await this.request.post(API.publicationRequestDetails, { - data: { url: publicationUrl }, - }); + const response = await this.request.post( + this.getHost(API.publicationRequestDetails), + { + data: { url: publicationUrl }, + }, + ); const statusCode = response.status(); expect( statusCode, @@ -41,9 +49,12 @@ export class PublicationApiHelper extends BaseApiHelper { public async approveRequest( publicationRequest: Publication | PublicationInfo, ) { - const response = await this.request.post(API.publicationRequestApproval, { - data: { url: publicationRequest.url }, - }); + const response = await this.request.post( + this.getHost(API.publicationRequestApproval), + { + data: { url: publicationRequest.url }, + }, + ); expect(response.status(), `Successfully approved publication request`).toBe( 200, ); @@ -52,9 +63,12 @@ export class PublicationApiHelper extends BaseApiHelper { public async rejectRequest( publicationRequest: Publication | PublicationInfo, ) { - const response = await this.request.post(API.publicationRequestRejection, { - data: { url: publicationRequest.url }, - }); + const response = await this.request.post( + this.getHost(API.publicationRequestRejection), + { + data: { url: publicationRequest.url }, + }, + ); expect(response.status(), `Successfully rejected publication request`).toBe( 200, ); @@ -71,9 +85,12 @@ export class PublicationApiHelper extends BaseApiHelper { requestModel.targetFolder, ); - const response = await this.request.post(API.publicationRequestCreate, { - data: requestModel, - }); + const response = await this.request.post( + this.getHost(API.publicationRequestCreate), + { + data: requestModel, + }, + ); expect(response.status(), `Successfully created publication request`).toBe( 200, ); @@ -97,9 +114,12 @@ export class PublicationApiHelper extends BaseApiHelper { resources: unpublishResources, rules: publicationRequest.rules, }; - const response = await this.request.post(API.publicationRequestCreate, { - data: data, - }); + const response = await this.request.post( + this.getHost(API.publicationRequestCreate), + { + data: data, + }, + ); expect(response.status(), `Successfully created unpublish request`).toBe( 200, ); diff --git a/apps/chat-e2e/src/testData/api/shareApiHelper.ts b/apps/chat-e2e/src/testData/api/shareApiHelper.ts index 2b3cb244c8..9e018a533f 100644 --- a/apps/chat-e2e/src/testData/api/shareApiHelper.ts +++ b/apps/chat-e2e/src/testData/api/shareApiHelper.ts @@ -65,9 +65,12 @@ export class ShareApiHelper extends BaseApiHelper { invitationType: ShareRequestType.link, resources: resources, }; - const response = await this.request.post(API.shareConversationHost, { - data: requestData, - }); + const response = await this.request.post( + this.getHost(API.shareConversationHost), + { + data: requestData, + }, + ); expect( response.status(), `Successfully created share invitation link`, @@ -85,9 +88,12 @@ export class ShareApiHelper extends BaseApiHelper { shareLinkResponse.invitationLink, ), }; - const response = await this.request.post(API.shareInviteAcceptanceHost, { - data: requestData, - }); + const response = await this.request.post( + this.getHost(API.shareInviteAcceptanceHost), + { + data: requestData, + }, + ); expect( response.status(), `Successfully accepted share invitation link`, @@ -112,7 +118,7 @@ export class ShareApiHelper extends BaseApiHelper { with: ShareRelations.me, order: 'popular_asc', }; - const response = await this.request.post(API.shareListing, { + const response = await this.request.post(this.getHost(API.shareListing), { data: requestData, }); const statusCode = response.status(); @@ -138,9 +144,12 @@ export class ShareApiHelper extends BaseApiHelper { const requestData: ShareRevokeRequestModel = { resources: entityUrls, }; - const response = await this.request.post(API.discardShareWithMeItem, { - data: requestData, - }); + const response = await this.request.post( + this.getHost(API.discardShareWithMeItem), + { + data: requestData, + }, + ); expect(response.status(), `Shared items successfully deleted`).toBe(200); } } diff --git a/apps/chat-e2e/src/testData/conversationHistory/conversationData.ts b/apps/chat-e2e/src/testData/conversationHistory/conversationData.ts index 623e7069c8..ab6c8d5c4f 100644 --- a/apps/chat-e2e/src/testData/conversationHistory/conversationData.ts +++ b/apps/chat-e2e/src/testData/conversationHistory/conversationData.ts @@ -580,6 +580,7 @@ export class ConversationData extends FolderData { attachmentUrl: string, model: DialAIEntityModel | string, folderName?: string, + name?: string, ) { const modelToUse = { id: typeof model === 'string' ? model : model.id }; const conversation = this.conversationBuilder.getConversation(); @@ -603,7 +604,7 @@ export class ConversationData extends FolderData { }, settings: settings, }; - const name = GeneratorUtil.randomString(10); + name = name ?? GeneratorUtil.randomString(10); let conversationBuilder = this.conversationBuilder .withName(name) diff --git a/apps/chat-e2e/src/testData/expectedConstants.ts b/apps/chat-e2e/src/testData/expectedConstants.ts index f10b0b40a3..27534a8c96 100644 --- a/apps/chat-e2e/src/testData/expectedConstants.ts +++ b/apps/chat-e2e/src/testData/expectedConstants.ts @@ -217,6 +217,10 @@ export const ExpectedConstants = { 'Template must have at least one variable', messageTemplateRequiredField: 'Please fill in this required field', messageTemplateMismatchTextErrorMessage: `Template doesn't match the message text`, + modelInfoTooltipTitle: 'Current agent:', + modelInfoTooltipChangeTitle: 'Change current agent:', + requestApiKeyLink: 'this form', + reportAnIssueLink: 'report an issue', }; export enum Types { diff --git a/apps/chat-e2e/src/testData/injector/apiInjector.ts b/apps/chat-e2e/src/testData/injector/apiInjector.ts index c05bd954ad..c87af256e6 100644 --- a/apps/chat-e2e/src/testData/injector/apiInjector.ts +++ b/apps/chat-e2e/src/testData/injector/apiInjector.ts @@ -28,7 +28,7 @@ export class ApiInjector implements DataInjectorInterface { await this.itemApiHelper.createConversations(conversations); } - async deleteAllData(isOverlay = false) { - await this.itemApiHelper.deleteAllData(undefined, isOverlay); + async deleteAllData() { + await this.itemApiHelper.deleteAllData(); } } diff --git a/apps/chat-e2e/src/testData/injector/dataInjectorInterface.ts b/apps/chat-e2e/src/testData/injector/dataInjectorInterface.ts index 7c1f6c06d7..be419ed46a 100644 --- a/apps/chat-e2e/src/testData/injector/dataInjectorInterface.ts +++ b/apps/chat-e2e/src/testData/injector/dataInjectorInterface.ts @@ -19,5 +19,5 @@ export interface DataInjectorInterface { prompts: Prompt[], ...folders: FolderInterface[] ): Promise; - deleteAllData(isOverlay?: boolean): Promise; + deleteAllData(): Promise; } diff --git a/apps/chat-e2e/src/testData/overlay/overlaySandboxUrls.ts b/apps/chat-e2e/src/testData/overlay/overlaySandboxUrls.ts index 1ded1a6308..bb72fda967 100644 --- a/apps/chat-e2e/src/testData/overlay/overlaySandboxUrls.ts +++ b/apps/chat-e2e/src/testData/overlay/overlaySandboxUrls.ts @@ -1,4 +1,28 @@ export const OverlaySandboxUrls = { - modelIdSetSandboxUrl: '/cases/overlay/model-id-set-sandbox', //sandbox to test 'EPMRTC-3781', 'EPMRTC-4693' + modelIdSetSandboxUrl: '/cases/overlay/model-id-set-sandbox', //sandbox to test 'EPMRTC-3781', 'EPMRTC-4693', 'EPMRTC-3770', 'EPMRTC-4438', 'EPMRTC-3782', 'EPMRTC-3762', 'EPMRTC-3763', 'EPMRTC-3764', 'EPMRTC-4873' overlayManagerUrl: '/cases/overlay-manager', //sandbox to test 'EPMRTC-1404' + disabledHeaderSandboxUrl: '/cases/overlay/disabled-header-sandbox', //sandbox to test 'EPMRTC-3759', 'EPMRTC-3760', 'EPMRTC-3769', 'EPMRTC-3768','EPMRTC-3771','EPMRTC-3772','EPMRTC-3775','EPMRTC-3776','EPMRTC-3777','EPMRTC-3778','EPMRTC-3779','EPMRTC-3767' + enabledHeaderSandboxUrl: '/cases/overlay/enabled-header-sandbox', //sandbox to test 'EPMRTC-3759', 'EPMRTC-3760', 'EPMRTC-3771', 'EPMRTC-3772', 'EPMRTC-3775', 'EPMRTC-3776', 'EPMRTC-3777', 'EPMRTC-3778', 'EPMRTC-3779', 'EPMRTC-4438', 'EPMRTC-3767' + enabledOnlyHeaderSandboxUrl: '/cases/overlay/enabled-only-header-sandbox', //sandbox to test 'EPMRTC-3766', 'EPMRTC-3767', 'EPMRTC-3778' + enabledOnlyHeaderFooterSandboxUrl: + '/cases/overlay/enabled-only-header-footer-sandbox', //sandbox to test 'EPMRTC-3768', 'EPMRTC-3769' + enabledOnlyFooterLinksAttachmentsUrl: + '/cases/overlay/enabled-only-footer-links-attachments-sandbox', //sandbox to test 'EPMRTC-3768', 'EPMRTC-3769', 'EPMRTC-3775' + enabledOnlyHeaderConversationsSectionSandboxUrl: + '/cases/overlay/enabled-only-header-conversations-section-sandbox', //sandbox to test 'EPMRTC-3775' + disableTopChatInfoUrl: '/cases/overlay/disabled-top-chat-info-sandbox', //sandbox to test 'EPMRTC-3762', 'EPMRTC-3763', 'EPMRTC-3764' + enableDisallowChangeAgentUrl: + '/cases/overlay/enabled-disallow-change-agent-sandbox', //sandbox to test 'EPMRTC-4872' + enableHideTopContextMenuUrl: + '/cases/overlay/enabled-hide-top-context-menu-sandbox', //sandbox to test 'EPMRTC-4873' + enableEmptyChatSettingsOverlayUrl: + '/cases/overlay/enabled-empty-chat-settings-sandbox', //sandbox to test 'EPMRTC-3773', 'EPMRTC-3765' + enableInputFilesUrl: '/cases/overlay/enabled-input-files-sandbox', //sandbox to test 'EPMRTC-3773' + enableHideEmptyChangeAgentUrl: + '/cases/overlay/enabled-hide-empty-change-agent-sandbox', //sandbox to test 'EPMRTC-4868' + disableAllFeaturesUrl: '/cases/overlay/disabled-all-features-sandbox', //sandbox to test 'EPMRTC-3780', 'EPMRTC-3765' + enableMarketplaceUrl: '/cases/overlay/enabled-marketplace-sandbox', //sandbox to test 'EPMRTC-4447', 'EPMRTC-4712' + disableMarketplaceUrl: '/cases/overlay/disabled-marketplace-sandbox', //sandbox to test 'EPMRTC-4867' + overlayConversationIdSetUrl: + '/cases/overlay/overlay-conversation-id-set-sandbox', //sandbox to test 'EPMRTC-4835' }; diff --git a/apps/chat-e2e/src/testData/publishing/publishRequestBuilder.ts b/apps/chat-e2e/src/testData/publishing/publishRequestBuilder.ts index 16734e0cd0..975ebcbcd3 100644 --- a/apps/chat-e2e/src/testData/publishing/publishRequestBuilder.ts +++ b/apps/chat-e2e/src/testData/publishing/publishRequestBuilder.ts @@ -70,12 +70,20 @@ export class PublishRequestBuilder { return this; } - withFileResource(attachment: Attachment): PublishRequestBuilder { - const resource = { - action: PublishActions.ADD, - sourceUrl: attachment.url, + withFileResource( + attachment: Attachment, + action: PublishActions, + ): PublishRequestBuilder { + let resource: PublicationResource = { + action: action, targetUrl: `files/${this.getPublishRequest().targetFolder}${attachment.title}`, }; + if (action === 'ADD' || action === 'ADD_IF_ABSENT') { + resource = { + ...resource, + sourceUrl: attachment.url, + }; + } this.publishRequest.resources.push(resource); return this; } diff --git a/apps/chat-e2e/src/tests/isolatedView.test.ts b/apps/chat-e2e/src/tests/isolatedView.test.ts index 2c0307c331..c24c7e767d 100644 --- a/apps/chat-e2e/src/tests/isolatedView.test.ts +++ b/apps/chat-e2e/src/tests/isolatedView.test.ts @@ -77,7 +77,7 @@ dialTest( await chat.sendRequestWithButton(request); await chatBar.waitForState({ state: 'hidden' }); await promptBar.waitForState({ state: 'hidden' }); - await chatHeader.openConversationSettings.waitForState({ + await chatHeader.conversationSettings.waitForState({ state: 'visible', }); }, @@ -277,7 +277,7 @@ dialTest( AccountMenuOptions.settings, AccountMenuOptions.logout, ); - await accountDropdownMenu.selectMenuOption('Settings'); + await accountDropdownMenu.selectMenuOption(AccountMenuOptions.settings); await baseAssertion.assertElementState(settingsModal, 'visible'); await baseAssertion.assertElementState(settingsModal.theme, 'visible'); await baseAssertion.assertElementState( @@ -303,7 +303,7 @@ dialTest( await dialTest.step( 'Click on the Settings icon after sending the request', async () => { - await chatHeader.openConversationSettings.click(); + await chatHeader.conversationSettings.click(); await baseAssertion.assertElementState( conversationSettingsModal.getElementLocator(), 'visible', @@ -316,7 +316,7 @@ dialTest( await dialTest.step( 'Hover over the Setting icon and check the wording on the tooltip', async () => { - await chatHeader.openConversationSettings.hoverOver(); + await chatHeader.conversationSettings.hoverOver(); const tooltipContent = await tooltip.getContent(); expect .soft(tooltipContent, ExpectedMessages.tooltipContentIsValid) @@ -384,7 +384,7 @@ dialTest( await dialTest.step( 'Click on the logo after sending the request', async () => { - await header.dialLogo.click(); + await header.logo.click(); await chatBar.waitForState({ state: 'hidden' }); await promptBar.waitForState({ state: 'hidden' }); await sendMessageAssertion.assertInputFieldState('visible', 'enabled'); diff --git a/apps/chat-e2e/src/tests/overlay/chatSettingsFeature.test.ts b/apps/chat-e2e/src/tests/overlay/chatSettingsFeature.test.ts new file mode 100644 index 0000000000..9c1e1d8a53 --- /dev/null +++ b/apps/chat-e2e/src/tests/overlay/chatSettingsFeature.test.ts @@ -0,0 +1,278 @@ +import { Conversation } from '@/chat/types/chat'; +import { DialAIEntityModel } from '@/chat/types/models'; +import dialTest from '@/src/core/dialFixtures'; +import dialOverlayTest from '@/src/core/dialOverlayFixtures'; +import { + Attachment, + MockedChatApiResponseBodies, + OverlaySandboxUrls, +} from '@/src/testData'; +import { GeneratorUtil, ModelsUtil } from '@/src/utils'; + +let modelWithAttachment: DialAIEntityModel; + +dialOverlayTest.beforeAll(async () => { + modelWithAttachment = GeneratorUtil.randomArrayElement( + ModelsUtil.getLatestModelsWithAttachment(), + ); +}); + +dialOverlayTest( + '[Overlay] Allow attach files to conversation - Feature.InputFiles. p1\n' + + '[Overlay] Display configure settings for empty chat - Feature.EmptyChatSettings. p2\n' + + '[Overlay] Display change agent for empty chat - Feature.HideEmptyChatChangeAgent. p1', + async ({ + overlayHomePage, + overlayChat, + overlayDataInjector, + overlayBaseAssertion, + conversationData, + overlaySendMessage, + overlayHeader, + overlayChatHeader, + overlayConfirmationDialog, + overlayConversations, + overlayConversationSettingsModal, + overlayToast, + overlayFileApiHelper, + setTestIds, + }) => { + setTestIds('EPMRTC-3773', 'EPMRTC-3765', 'EPMRTC-4868'); + + let attachmentConversation: Conversation; + + await dialTest.step( + 'Create conversation with attachment in the request', + async () => { + const imageUrl = await overlayFileApiHelper.putFile( + Attachment.sunImageName, + ); + attachmentConversation = + conversationData.prepareConversationWithAttachmentsInRequest( + modelWithAttachment, + true, + imageUrl, + ); + await overlayDataInjector.createConversations([attachmentConversation]); + }, + ); + + await dialTest.step( + 'Open conversation with attachment and verify clip icon is not available in the Send field', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enableEmptyChatSettingsOverlayUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayHeader.leftPanelToggle.click(); + await overlayConversations.selectConversation( + attachmentConversation.name, + ); + await overlayChat.addModelToWorkspace(); + await overlayToast.closeToast(); + await overlayBaseAssertion.assertElementState( + overlaySendMessage.attachmentMenuTrigger, + 'hidden', + ); + }, + ); + + await dialTest.step( + 'Verify gear icon is not available in the header', + async () => { + await overlayBaseAssertion.assertElementState( + overlayChatHeader.conversationSettings, + 'hidden', + ); + }, + ); + + await dialTest.step( + 'Clear all conversation messages and verify "Configure settings" link is available', + async () => { + await overlayChatHeader.clearConversation.click(); + await overlayConfirmationDialog.confirm({ triggeredHttpMethod: 'PUT' }); + await overlayBaseAssertion.assertElementState( + overlayChat.configureSettingsButton, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Create a new conversation and verify "Configure settings", "Change agent" links are available', + async () => { + await overlayHeader.createNewConversation(); + await overlayBaseAssertion.assertElementState( + overlayChat.configureSettingsButton, + 'visible', + ); + + await overlayBaseAssertion.assertElementState( + overlayChat.changeAgentButton, + 'visible', + ); + await overlayChat.configureSettingsButton.click(); + await overlayBaseAssertion.assertElementState( + overlayConversationSettingsModal, + 'visible', + ); + await overlayConversationSettingsModal.cancelButton.click(); + }, + ); + }, +); + +dialOverlayTest( + '[Overlay] Allow attach files to conversation - Feature.InputFiles. p2', + async ({ + overlayHomePage, + overlayChat, + overlayDataInjector, + overlayBaseAssertion, + conversationData, + overlaySendMessage, + overlayHeader, + overlayConversations, + setTestIds, + }) => { + setTestIds('EPMRTC-3773'); + + let attachmentConversation: Conversation; + + await dialTest.step( + 'Create conversation with attachment in the request', + async () => { + attachmentConversation = + conversationData.prepareConversationWithAttachmentsInRequest( + modelWithAttachment, + true, + ); + await overlayDataInjector.createConversations([attachmentConversation]); + }, + ); + + await dialTest.step( + 'Open conversation with attachment and verify clip icon is available in the Send field', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enableInputFilesUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayHeader.leftPanelToggle.click(); + await overlayConversations.selectConversation( + attachmentConversation.name, + ); + await overlayChat.addModelToWorkspace(); + await overlayBaseAssertion.assertElementState( + overlaySendMessage.attachmentMenuTrigger, + 'visible', + ); + }, + ); + }, +); + +dialOverlayTest( + `[Overlay] Display change agent for empty chat - Feature.HideEmptyChatChangeAgent. p2`, + async ({ + overlayHomePage, + overlayChat, + overlayBaseAssertion, + setTestIds, + }) => { + setTestIds('EPMRTC-4868'); + + await dialTest.step( + 'Open sandbox and verify "Change agent" link is not displayed for a new conversation', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enableHideEmptyChangeAgentUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayBaseAssertion.assertElementState( + overlayChat.changeAgentButton, + 'hidden', + ); + }, + ); + }, +); + +dialOverlayTest( + `[Overlay] When no any feature is enabled in the code.\n` + + '[Overlay] Display configure settings for empty chat - Feature.EmptyChatSettings. p1', + async ({ + overlayHomePage, + overlayChat, + overlayChatMessages, + overlayChatMessagesAssertion, + overlayBaseAssertion, + overlayAgentInfo, + overlaySendMessage, + setTestIds, + }) => { + setTestIds('EPMRTC-3780', 'EPMRTC-3765'); + + await dialTest.step( + 'Open sandbox and verify model information, send request field and "Change agent" link are available', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.disableAllFeaturesUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayBaseAssertion.assertElementState( + overlayAgentInfo, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlaySendMessage, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayAgentInfo.agentName, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayAgentInfo.agentDescription, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChat.changeAgentButton, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChat.configureSettingsButton, + 'hidden', + ); + }, + ); + + await dialTest.step( + 'Send the request and verify Edit, Delete, Copy and Regenerate buttons are available for the response', + async () => { + await overlayHomePage.mockChatTextResponse( + MockedChatApiResponseBodies.simpleTextBody, + { isOverlay: true }, + ); + await overlayChat.sendRequestWithButton('test'); + await overlayChatMessagesAssertion.assertMessageDeleteIconState( + 1, + 'visible', + ); + await overlayChatMessagesAssertion.assertMessageEditIconState( + 1, + 'visible', + ); + await overlayChatMessagesAssertion.assertElementState( + overlayChatMessages.messageCopyIcon(2), + 'visible', + ); + await overlayChatMessagesAssertion.assertElementState( + overlayChatMessages.messageRegenerateIcon(2), + 'visible', + ); + }, + ); + }, +); diff --git a/apps/chat-e2e/src/tests/overlay/headerFeature.test.ts b/apps/chat-e2e/src/tests/overlay/headerFeature.test.ts new file mode 100644 index 0000000000..30d672f793 --- /dev/null +++ b/apps/chat-e2e/src/tests/overlay/headerFeature.test.ts @@ -0,0 +1,637 @@ +import { Conversation } from '@/chat/types/chat'; +import { Prompt } from '@/chat/types/prompt'; +import dialTest from '@/src/core/dialFixtures'; +import dialOverlayTest from '@/src/core/dialOverlayFixtures'; +import { + API, + ExpectedConstants, + ExpectedMessages, + MenuOptions, + MockedChatApiResponseBodies, + Theme, +} from '@/src/testData'; +import { OverlaySandboxUrls } from '@/src/testData/overlay/overlaySandboxUrls'; +import { keys } from '@/src/ui/keyboard'; +import { GeneratorUtil, ItemUtil, ModelsUtil } from '@/src/utils'; +import { Locator, expect } from '@playwright/test'; + +dialOverlayTest( + '[Overlay] Defaults set in the code: theme.\n' + + '[Overlay] Display conversations panel - Feature.ConversationsSection.\n' + + '[Overlay] Display prompts panel - Feature.PromptsSection.\n' + + '[Overlay] Display Report an issue modal - Feature.ReportAnIssue.\n' + + '[Overlay] Display Request API Key modal - Feature.RequestApiKey.\n' + + '[Overlay] Display conversation sharing - ConversationsSharing.\n' + + '[Overlay] Display prompts sharing - PromptsSharing.\n' + + '[Overlay] Display attachments manager in conversation panel - Feature.AttachmentsManager.\n' + + '[Overlay] Display conversation publishing - Feature.ConversationsPublishing.\n' + + '[Overlay] Display prompts publishing - Feature.PromptsPublishing.\n' + + '[Overlay] Enable setting for custom logo feature - Feature.CustomLogo.\n' + + '[Overlay] Display DIAL footer - Feature.Footer', + async ({ + overlayHomePage, + overlayAgentInfo, + overlayChat, + overlayHeader, + overlaySendMessage, + overlayAccountSettings, + overlayBaseAssertion, + overlayAssertion, + setTestIds, + }) => { + setTestIds( + 'EPMRTC-3782', + 'EPMRTC-3759', + 'EPMRTC-3760', + 'EPMRTC-3769', + 'EPMRTC-3768', + 'EPMRTC-3771', + 'EPMRTC-3772', + 'EPMRTC-3775', + 'EPMRTC-3776', + 'EPMRTC-3777', + 'EPMRTC-3778', + 'EPMRTC-3767', + ); + + await dialTest.step('Verify header is not visible', async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.disabledHeaderSandboxUrl, + ); + await overlayBaseAssertion.assertElementState(overlayHeader, 'hidden'); + }); + + await dialTest.step( + 'Verify new conversation with possibility to change agent, configure and send request are available', + async () => { + await overlayBaseAssertion.assertElementState( + overlayAgentInfo, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChat.changeAgentButton, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChat.configureSettingsButton, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChat.getSendMessage(), + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlaySendMessage, + 'visible', + ); + }, + ); + + await dialTest.step('Verify Light theme is set', async () => { + await overlayAssertion.assertOverlayTheme(overlayHomePage, Theme.light); + }); + + await dialTest.step( + 'Verify side bar toggles and account settings are not available', + async () => { + await overlayBaseAssertion.assertElementState( + overlayHeader.leftPanelToggle, + 'hidden', + ); + await overlayBaseAssertion.assertElementState( + overlayHeader.rightPanelToggle, + 'hidden', + ); + await overlayBaseAssertion.assertElementState( + overlayAccountSettings, + 'hidden', + ); + }, + ); + }, +); + +dialOverlayTest( + '[Overlay] Display conversations panel - Feature.ConversationsSection.\n' + + '[Overlay] Display prompts panel - Feature.PromptsSection.\n' + + '[Overlay] Display conversation sharing - ConversationsSharing.\n' + + '[Overlay] Display prompts sharing - PromptsSharing.\n' + + '[Overlay] Display attachments manager in conversation panel - Feature.AttachmentsManager.\n' + + '[Overlay] Display conversation publishing - Feature.ConversationsPublishing.\n' + + '[Overlay] Display prompts publishing - Feature.PromptsPublishing.\n' + + '[Overlay] Enable setting for custom logo feature - Feature.CustomLogo.\n' + + '[Overlay] Hide "New conversation" button in DIAL header - Feature.HideNewConversation.\n' + + '[Overlay] Message template feature toggle - Feature.MessageTemplates.\n' + + '[Overlay] Display DIAL footer - Feature.Footer', + async ({ + page, + overlayHomePage, + overlayHeader, + overlayChatBar, + overlayConversations, + overlayConversationDropdownMenu, + overlayPublishingRequestModal, + overlayPrompts, + overlayPromptDropdownMenu, + overlaySettingsModal, + overlayAgentInfo, + overlayShareModal, + overlayAccountSettings, + overlayProfilePanel, + overlayRequestApiKeyModal, + overlayReportAnIssueModal, + overlayAttachFilesModal, + overlayChatMessages, + overlayBaseAssertion, + overlayConversationAssertion, + overlayPromptAssertion, + conversationData, + promptData, + overlayDataInjector, + setTestIds, + }) => { + setTestIds( + 'EPMRTC-3759', + 'EPMRTC-3760', + 'EPMRTC-3771', + 'EPMRTC-3772', + 'EPMRTC-3775', + 'EPMRTC-3776', + 'EPMRTC-3777', + 'EPMRTC-3778', + 'EPMRTC-3779', + 'EPMRTC-4438', + 'EPMRTC-3767', + ); + let conversation: Conversation; + let prompt: Prompt; + let conversationEntity: Locator; + let promptEntity: Locator; + + await dialTest.step('Create simple conversation and prompt', async () => { + conversation = conversationData.prepareDefaultConversation(); + prompt = promptData.preparePrompt('test'); + await overlayDataInjector.createConversations([conversation]); + await overlayDataInjector.createPrompts([prompt]); + }); + + await dialTest.step('Verify header is visible', async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enabledHeaderSandboxUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayBaseAssertion.assertElementState(overlayHeader, 'visible'); + }); + + await dialTest.step('Verify side bar toggles are available', async () => { + await overlayBaseAssertion.assertElementState( + overlayHeader.leftPanelToggle, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayHeader.rightPanelToggle, + 'visible', + ); + }); + + await dialTest.step( + 'Verify "New Conversation button" is not available', + async () => { + await overlayBaseAssertion.assertElementState( + overlayHeader.newEntityButton, + 'hidden', + ); + }, + ); + + await dialTest.step( + 'Open left side panel and verify created conversation is visible', + async () => { + await overlayHeader.leftPanelToggle.click(); + await overlayConversationAssertion.assertEntityState( + { name: conversation.name }, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Verify "Manage attachments" modal is opened on click "Clip" in the bottom menu', + async () => { + await overlayBaseAssertion.assertElementState( + overlayChatBar.attachments, + 'visible', + ); + await overlayChatBar.openManageAttachmentsModal(); + await overlayBaseAssertion.assertElementState( + overlayAttachFilesModal, + 'visible', + ); + await overlayAttachFilesModal.closeButton.click(); + }, + ); + + await dialTest.step( + 'Verify Share option is available for conversation', + async () => { + conversationEntity = overlayConversations.getTreeEntity( + conversation.name, + ); + await conversationEntity.hover(); + await overlayConversations.entityDotsMenu(conversation.name).click(); + await overlayConversationDropdownMenu.selectShareMenuOption(); + await overlayBaseAssertion.assertElementState( + overlayShareModal, + 'visible', + ); + await overlayShareModal.closeButton.click(); + }, + ); + + await dialTest.step( + 'Verify Publish option is available for conversation', + async () => { + await conversationEntity.hover(); + await overlayConversations.entityDotsMenu(conversation.name).click(); + await overlayConversationDropdownMenu.selectMenuOption( + MenuOptions.publish, + ); + await overlayBaseAssertion.assertElementState( + overlayPublishingRequestModal, + 'visible', + ); + await overlayPublishingRequestModal.cancelButton.click(); + }, + ); + + await dialTest.step( + 'Verify new conversation is not create if to click on app logo', + async () => { + await overlayConversations.selectConversation( + conversation.name, + { exactMatch: true }, + { isHttpMethodTriggered: false }, + ); + await overlayHeader.logo.click(); + await overlayBaseAssertion.assertElementState( + overlayAgentInfo, + 'hidden', + ); + }, + ); + + await dialTest.step( + 'Verify "Set message template" button is available for the request', + async () => { + const request = await overlayChatMessages.hoverOverMessage(1); + await overlayBaseAssertion.assertElementState( + overlayChatMessages.setMessageTemplateIcon(request), + 'visible', + ); + }, + ); + + await dialTest.step( + 'Open right side panel and verify created prompt is visible', + async () => { + await overlayHeader.rightPanelToggle.click(); + await overlayPromptAssertion.assertEntityState( + { name: prompt.name }, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Verify Share option is available for prompt', + async () => { + promptEntity = overlayPrompts.getTreeEntity(prompt.name); + await promptEntity.hover(); + await overlayPrompts.entityDotsMenu(prompt.name).click(); + await overlayPromptDropdownMenu.selectShareMenuOption(); + await overlayBaseAssertion.assertElementState( + overlayShareModal, + 'visible', + ); + await overlayShareModal.closeButton.click(); + }, + ); + + await dialTest.step( + 'Verify Publish option is available for prompt', + async () => { + await promptEntity.hover(); + await overlayPrompts.entityDotsMenu(prompt.name).click(); + await overlayPromptDropdownMenu.selectMenuOption(MenuOptions.publish); + await overlayBaseAssertion.assertElementState( + overlayPublishingRequestModal, + 'visible', + ); + await overlayPublishingRequestModal.cancelButton.click(); + }, + ); + + await dialTest.step( + 'Verify custom logo can be set, footer is displayed', + async () => { + await overlayAccountSettings.click(); + await overlayBaseAssertion.assertElementState( + overlayProfilePanel.getFooter(), + 'visible', + ); + await overlayProfilePanel.settings.click(); + await overlayBaseAssertion.assertElementState( + overlaySettingsModal.customLogo, + 'visible', + ); + await overlaySettingsModal.cancelButton.click(); + }, + ); + + await dialTest.step('Verify API key can be requested', async () => { + await overlayProfilePanel + .getFooter() + .openFooterLink(ExpectedConstants.requestApiKeyLink); + //TODO: remove when fixed https://github.com/epam/ai-dial-chat/issues/2878 + const overlayRequestApiKeyModalFirst = + overlayRequestApiKeyModal.getNthElement(0); + await overlayBaseAssertion.assertElementState( + overlayRequestApiKeyModalFirst, + 'visible', + ); + for (let i = 1; i <= 2; i++) { + await page.keyboard.press(keys.escape); + } + }); + + await dialTest.step('Verify a new issue can be reported', async () => { + await overlayProfilePanel + .getFooter() + .openFooterLink(ExpectedConstants.reportAnIssueLink); + //TODO: remove when fixed https://github.com/epam/ai-dial-chat/issues/2878 + const overlayRequestApiKeyModalFirst = + overlayReportAnIssueModal.getNthElement(0); + await overlayBaseAssertion.assertElementState( + overlayRequestApiKeyModalFirst, + 'visible', + ); + }); + }, +); + +dialOverlayTest( + '[Overlay] Display DIAL header - Feature.Header.\n' + + '[Overlay] Display DIAL footer - Feature.Footer.\n' + + '[Overlay] Enable setting for custom logo feature - Feature.CustomLogo', + async ({ + overlayHomePage, + overlayAgentInfo, + overlayChat, + overlayHeader, + overlayAccountSettings, + overlayBaseAssertion, + overlayItemApiHelper, + overlayProfilePanel, + overlayConfirmationDialog, + overlaySettingsModal, + setTestIds, + }) => { + setTestIds('EPMRTC-3766', 'EPMRTC-3767', 'EPMRTC-3778'); + + await dialTest.step( + 'Verify "New Conversation", logo and profile setting buttons are available in the header', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enabledOnlyHeaderSandboxUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayBaseAssertion.assertElementState( + overlayHeader.newEntityButton, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayHeader.logo, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayAccountSettings, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Verify new conversation is create on click "Plus" button', + async () => { + await overlayHomePage.mockChatTextResponse( + MockedChatApiResponseBodies.simpleTextBody, + { isOverlay: true }, + ); + const requestContent = GeneratorUtil.randomString(5); + await overlayChat.sendRequestWithButton(requestContent); + await overlayHeader.createNewConversation(); + await overlayBaseAssertion.assertElementState( + overlayAgentInfo, + 'visible', + ); + const allConversations = await overlayItemApiHelper.listItems( + API.conversationsHost(), + ); + const conversationWithContent = `${ModelsUtil.getDefaultModel()!.id}${ItemUtil.conversationIdSeparator}${requestContent}`; + expect + .soft( + allConversations.find((c) => c.name === conversationWithContent), + ExpectedMessages.conversationIsVisible, + ) + .toBeDefined(); + }, + ); + + await dialTest.step( + 'Verify new conversation is create on click logo button', + async () => { + await overlayHomePage.mockChatTextResponse( + MockedChatApiResponseBodies.simpleTextBody, + { isOverlay: true }, + ); + await overlayChat.sendRequestWithButton(GeneratorUtil.randomString(5)); + await overlayHeader.logo.click(); + await overlayBaseAssertion.assertElementState( + overlayAgentInfo, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Open profile settings and verify user info, "Settings", "Logout" options are displayed', + async () => { + await overlayAccountSettings.click(); + await overlayBaseAssertion.assertElementState( + overlayProfilePanel.settings, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayProfilePanel.logout, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayProfilePanel.username, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayProfilePanel.avatar, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayAccountSettings.avatarIcon, + 'hidden', + ); + await overlayBaseAssertion.assertElementState( + overlayAccountSettings.closeButton, + 'visible', + ); + }, + ); + + await dialTest.step('Verify footer is not visible', async () => { + await overlayBaseAssertion.assertElementState( + overlayProfilePanel.getFooter(), + 'hidden', + ); + }); + + await dialTest.step( + 'Click on "Log out" and verify confirmation dialog appears', + async () => { + await overlayProfilePanel.logout.click(); + await overlayBaseAssertion.assertElementState( + overlayConfirmationDialog, + 'visible', + ); + await overlayConfirmationDialog.cancelDialog(); + }, + ); + + await dialTest.step( + 'Open profile settings and verify "Custom logo" cannot be set', + async () => { + await overlayProfilePanel.settings.click(); + await overlayBaseAssertion.assertElementState( + overlaySettingsModal.customLogo, + 'hidden', + ); + await overlaySettingsModal.cancelButton.click(); + }, + ); + + await dialTest.step( + 'Click on "X" and verify profile panel is closed', + async () => { + await overlayAccountSettings.closeButton.click(); + await overlayBaseAssertion.assertElementState( + overlayProfilePanel, + 'hidden', + ); + await overlayBaseAssertion.assertElementState( + overlayAccountSettings.avatarIcon, + 'visible', + ); + }, + ); + }, +); + +dialOverlayTest( + '[Overlay] Display Request API Key modal - Feature.RequestApiKey.\n' + + '[Overlay] Display Report an issue modal - Feature.ReportAnIssue', + async ({ + overlayHomePage, + overlayRequestApiKeyModal, + overlayAccountSettings, + overlayBaseAssertion, + overlayProfilePanel, + overlayReportAnIssueModal, + setTestIds, + }) => { + setTestIds('EPMRTC-3768', 'EPMRTC-3769'); + + await dialTest.step( + 'Open profile panel and verify API key cannot be requested', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enabledOnlyHeaderFooterSandboxUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayAccountSettings.click(); + await overlayProfilePanel + .getFooter() + .openFooterLink(ExpectedConstants.requestApiKeyLink); + await overlayBaseAssertion.assertElementState( + overlayRequestApiKeyModal, + 'hidden', + ); + }, + ); + + await dialTest.step('Verify new issue cannot be reported', async () => { + await overlayProfilePanel + .getFooter() + .openFooterLink(ExpectedConstants.reportAnIssueLink); + await overlayBaseAssertion.assertElementState( + overlayReportAnIssueModal, + 'hidden', + ); + }); + }, +); + +dialOverlayTest( + '[Overlay] Display Request API Key modal - Feature.RequestApiKey.\n' + + '[Overlay] Display Report an issue modal - Feature.ReportAnIssue.\n' + + '[Overlay] Display attachments manager in conversation panel - Feature.AttachmentsManager', + async ({ + overlayHomePage, + overlayHeader, + overlayBaseAssertion, + setTestIds, + }) => { + setTestIds('EPMRTC-3768', 'EPMRTC-3769', 'EPMRTC-3775'); + + await dialTest.step('Verify header is not available', async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enabledOnlyFooterLinksAttachmentsUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayBaseAssertion.assertElementState(overlayHeader, 'hidden'); + }); + }, +); + +dialOverlayTest( + '[Overlay] Display attachments manager in conversation panel - Feature.AttachmentsManager', + async ({ + overlayHomePage, + overlayHeader, + overlayBaseAssertion, + overlayChatBar, + setTestIds, + }) => { + setTestIds('EPMRTC-3775'); + + await dialTest.step( + 'Open conversations side panel and verify "Clip" icon is not visible at the bottom panel', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enabledOnlyHeaderConversationsSectionSandboxUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayHeader.leftPanelToggle.click(); + await overlayBaseAssertion.assertElementState( + overlayChatBar.attachments, + 'hidden', + ); + }, + ); + }, +); diff --git a/apps/chat-e2e/src/tests/overlay/marketplaceFeature.test.ts b/apps/chat-e2e/src/tests/overlay/marketplaceFeature.test.ts new file mode 100644 index 0000000000..27a76b6507 --- /dev/null +++ b/apps/chat-e2e/src/tests/overlay/marketplaceFeature.test.ts @@ -0,0 +1,157 @@ +import { Conversation } from '@/chat/types/chat'; +import dialTest from '@/src/core/dialFixtures'; +import dialOverlayTest from '@/src/core/dialOverlayFixtures'; +import { OverlaySandboxUrls } from '@/src/testData'; + +dialOverlayTest( + '[Overlay] DIAL Marketplace feature is enabled - Feature.Marketplace.\n' + + '[Overlay] Add app button is not available in Overlay (Mobile view)', + async ({ + overlayHomePage, + overlayChat, + overlayChatHeader, + overlayTalkToAgentDialog, + overlayHeader, + overlayConversations, + overlayMarketplacePage, + conversationData, + overlayBaseAssertion, + overlayTalkToAgentDialogAssertion, + overlayDataInjector, + overlayChatBar, + setTestIds, + }) => { + setTestIds('EPMRTC-4447', 'EPMRTC-4712'); + + let conversation: Conversation; + + await dialTest.step('Create simple conversation', async () => { + conversation = conversationData.prepareDefaultConversation(); + await overlayDataInjector.createConversations([conversation]); + }); + + await dialTest.step( + 'Verify "Dial Marketplace" button is available on the left side panel', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enableMarketplaceUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayHeader.leftPanelToggle.click(); + await overlayBaseAssertion.assertElementState( + overlayChatBar.dialMarketplaceLink, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Click on "Change agent" and verify there is "Search" field available', + async () => { + await overlayHeader.leftPanelToggle.click(); + await overlayChat.changeAgentButton.click(); + await overlayTalkToAgentDialogAssertion.assertElementState( + overlayTalkToAgentDialog.searchAgentInput, + 'visible', + ); + await overlayTalkToAgentDialog.cancelButton.click(); + }, + ); + + await dialTest.step( + 'Select created conversation, click on model icon in the header and verify there is "Search" field available', + async () => { + await overlayHeader.leftPanelToggle.click(); + await overlayConversations.selectConversation(conversation.name); + await overlayChatHeader.chatModelIcon.click(); + await overlayTalkToAgentDialogAssertion.assertElementState( + overlayTalkToAgentDialog.searchAgentInput, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Click on "Go to my workspace" link and verify workspace page is opened, "Add app" button is not visible', + async () => { + await overlayTalkToAgentDialog.goToMyWorkspace(); + const marketplace = overlayMarketplacePage + .getMarketplaceContainer() + .getMarketplace(); + await overlayBaseAssertion.assertElementState(marketplace, 'visible'); + await overlayBaseAssertion.assertElementState( + marketplace.getMarketplaceHeader().addAppButton, + 'hidden', + ); + }, + ); + }, +); + +dialOverlayTest( + '[Overlay] DIAL Marketplace feature is disabled - Feature.Marketplace', + async ({ + overlayHomePage, + overlayChat, + overlayChatHeader, + overlayTalkToAgentDialog, + overlayHeader, + overlayConversations, + conversationData, + overlayBaseAssertion, + overlayTalkToAgentDialogAssertion, + overlayDataInjector, + overlayChatBar, + setTestIds, + }) => { + setTestIds('EPMRTC-4867'); + + let conversation: Conversation; + + await dialTest.step('Create simple conversation', async () => { + conversation = conversationData.prepareDefaultConversation(); + await overlayDataInjector.createConversations([conversation]); + }); + + await dialTest.step( + 'Verify "Dial Marketplace" button is not available on the right side panel', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.disableMarketplaceUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayHeader.leftPanelToggle.click(); + await overlayBaseAssertion.assertElementState( + overlayChatBar.dialMarketplaceLink, + 'hidden', + ); + }, + ); + + await dialTest.step( + 'Click on "Change agent" and verify there is no "Go to My workspace" button', + async () => { + await overlayHeader.leftPanelToggle.click(); + await overlayChat.changeAgentButton.click(); + await overlayTalkToAgentDialogAssertion.assertElementState( + overlayTalkToAgentDialog.goToMyWorkspaceButton, + 'hidden', + ); + await overlayTalkToAgentDialog.cancelButton.click(); + }, + ); + + await dialTest.step( + 'Select created conversation, click on model icon in the header and verify there is no "Go to My workspace" button', + async () => { + await overlayHeader.leftPanelToggle.click(); + await overlayConversations.selectConversation(conversation.name); + await overlayChatHeader.chatModelIcon.click(); + await overlayTalkToAgentDialogAssertion.assertElementState( + overlayTalkToAgentDialog.goToMyWorkspaceButton, + 'hidden', + ); + }, + ); + }, +); diff --git a/apps/chat-e2e/src/tests/overlay/modelIdFeature.test.ts b/apps/chat-e2e/src/tests/overlay/modelIdFeature.test.ts index 6f080e03d1..6c19fea898 100644 --- a/apps/chat-e2e/src/tests/overlay/modelIdFeature.test.ts +++ b/apps/chat-e2e/src/tests/overlay/modelIdFeature.test.ts @@ -1,6 +1,12 @@ import dialTest from '@/src/core/dialFixtures'; import dialOverlayTest from '@/src/core/dialOverlayFixtures'; -import { ExpectedMessages, MockedChatApiResponseBodies } from '@/src/testData'; +import { + ExpectedConstants, + ExpectedMessages, + MockedChatApiResponseBodies, + Rate, + Theme, +} from '@/src/testData'; import { OverlaySandboxUrls } from '@/src/testData/overlay/overlaySandboxUrls'; import { GeneratorUtil, ModelsUtil } from '@/src/utils'; import { expect } from '@playwright/test'; @@ -9,12 +15,21 @@ const expectedModelId = 'gpt-4'; dialOverlayTest( `[Overlay] Defaults set in the code: modelID is used for new conversation.\n` + - '[Overlay] Defaults set in the code: modelID is NOT used for old conversation. Used model is used in the chat with history', + '[Overlay] Defaults set in the code: modelID is NOT used for old conversation. Used model is used in the chat with history.\n' + + '[Overlay] Display likes in model response - Feature.Likes.\n' + + '[Overlay] Message template feature toggle - Feature.MessageTemplates.\n' + + '[Overlay] Defaults set in the code: theme' + + '[Overlay] Display clear conversations button in chat header - Feature.TopClearConversation.\n' + + '[Overlay] Display conversation info in chat header - Feature.TopChatInfo.\n' + + '[Overlay] Display change model settings button in chat header - Feature.TopChatModelSettings.\n' + + '[Overlay] Display chat menu in chat header - Feature.HideTopContextMenu', async ({ overlayHomePage, overlayAgentInfo, overlayChat, + overlayChatMessages, overlayChatHeader, + overlayModelInfoTooltip, overlayTalkToAgentDialog, overlayHeader, overlayConversations, @@ -23,10 +38,21 @@ dialOverlayTest( overlayBaseAssertion, overlayApiAssertion, overlayAgentInfoAssertion, - talkToAgentDialogAssertion, + overlayTalkToAgentDialogAssertion, + overlayAssertion, setTestIds, }) => { - setTestIds('EPMRTC-3781', 'EPMRTC-4693'); + setTestIds( + 'EPMRTC-3781', + 'EPMRTC-4693', + 'EPMRTC-3770', + 'EPMRTC-4438', + 'EPMRTC-3782', + 'EPMRTC-3762', + 'EPMRTC-3763', + 'EPMRTC-3764', + 'EPMRTC-4873', + ); const randomAgentRequest = 'test'; const randomModelId = GeneratorUtil.randomArrayElement( ModelsUtil.getRecentModelIds().filter((m) => m !== expectedModelId), @@ -77,6 +103,43 @@ dialOverlayTest( }, ); + await dialTest.step( + 'Verify dots menu, "Clear conversation messages", model name, model and gear icons are available in the chat header', + async () => { + await overlayBaseAssertion.assertElementState( + overlayChatHeader.dotsMenu, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChatHeader.chatTitle, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChatHeader.chatModelIcon, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChatHeader.conversationSettings, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChatHeader.clearConversation, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Hover over model icon and verify tooltip content', + async () => { + await overlayChatHeader.chatModelIcon.hoverOver(); + await overlayBaseAssertion.assertElementText( + overlayModelInfoTooltip.title, + ExpectedConstants.modelInfoTooltipChangeTitle, + ); + }, + ); + await dialTest.step( 'Create new conversation and verify configured model is pre-set', async () => { @@ -92,7 +155,9 @@ dialOverlayTest( 'Open "Select an agent" modal and verify configured model is selected and is on top', async () => { await overlayChat.changeAgentButton.click(); - await talkToAgentDialogAssertion.assertAgentIsSelected(expectedModel); + await overlayTalkToAgentDialogAssertion.assertAgentIsSelected( + expectedModel, + ); const agents = await overlayTalkToAgentDialog .getAgents() .getAgentNames(); @@ -112,16 +177,43 @@ dialOverlayTest( }, ); + await dialTest.step( + 'Verify like/dislike button is available for the response', + async () => { + for (const rate of Object.values(Rate)) { + await overlayBaseAssertion.assertElementActionabilityState( + overlayChatMessages.getChatMessageRate(2, rate), + 'enabled', + ); + } + }, + ); + + await dialTest.step( + 'Verify "Set message template" button is not available for the request', + async () => { + const request = await overlayChatMessages.hoverOverMessage(1); + await overlayBaseAssertion.assertElementState( + overlayChatMessages.setMessageTemplateIcon(request), + 'hidden', + ); + }, + ); + await dialTest.step( 'Open "Select an agent" modal for the previous conversation and verify random model is selected', async () => { await overlayHeader.leftPanelToggle.click(); await overlayConversations.selectConversation(randomAgentRequest); await overlayChatHeader.chatModelIcon.click(); - await talkToAgentDialogAssertion.assertAgentIsSelected( + await overlayTalkToAgentDialogAssertion.assertAgentIsSelected( randomModel.name, ); }, ); + + await dialTest.step('Verify Dark theme is set', async () => { + await overlayAssertion.assertOverlayTheme(overlayHomePage, Theme.dark); + }); }, ); diff --git a/apps/chat-e2e/src/tests/overlay/overlayAuth.ts b/apps/chat-e2e/src/tests/overlay/overlayAuth.ts index 9173097194..b876767b70 100644 --- a/apps/chat-e2e/src/tests/overlay/overlayAuth.ts +++ b/apps/chat-e2e/src/tests/overlay/overlayAuth.ts @@ -10,6 +10,10 @@ const overlayUsernames = process.env .E2E_OVERLAY_USERNAME!.split(',') .slice(0, +config.workers!); +if (process.env.E2E_ADMIN) { + overlayUsernames.push(process.env.E2E_ADMIN); +} + for (let i = 0; i < overlayUsernames.length; i++) { // eslint-disable-next-line playwright/expect-expect test(`[Overlay] Login: ${overlayUsernames[i]}`, async ({ diff --git a/apps/chat-e2e/src/tests/overlay/overlayConversationIdFeature.test.ts b/apps/chat-e2e/src/tests/overlay/overlayConversationIdFeature.test.ts new file mode 100644 index 0000000000..b173e07cf4 --- /dev/null +++ b/apps/chat-e2e/src/tests/overlay/overlayConversationIdFeature.test.ts @@ -0,0 +1,189 @@ +import { Conversation } from '@/chat/types/chat'; +import { Publication } from '@/chat/types/publication'; +import dialOverlayTest from '@/src/core/dialOverlayFixtures'; +import { + API, + Attachment, + ExpectedConstants, + ExpectedMessages, + OverlaySandboxUrls, +} from '@/src/testData'; +import { keys } from '@/src/ui/keyboard'; +import { GeneratorUtil, ModelsUtil } from '@/src/utils'; +import { PublishActions } from '@epam/ai-dial-shared'; +import { expect } from '@playwright/test'; + +const publicationsToUnpublish: Publication[] = []; +const conversationName = 'overlayConversationName'; +const expectedConversationId = `conversations/public/playback__${ExpectedConstants.playbackConversation}${conversationName}__${ExpectedConstants.defaultAppVersion}`; + +dialOverlayTest( + '[Overlay] Exact conversation is set in Overlay. Playback chat with Plotly graph', + async ({ + overlayHomePage, + page, + overlayPlaybackControl, + overlayChatMessages, + overlayHeader, + conversationData, + overlayBaseAssertion, + localStorageManager, + overlayDataInjector, + setTestIds, + overlayFileApiHelper, + overlayPublicationApiHelper, + adminPublicationApiHelper, + publishRequestBuilder, + }) => { + setTestIds('EPMRTC-4835'); + let plotlyConversation: Conversation; + let playbackConversation: Conversation; + let plotlyImageUrl: string; + + await dialOverlayTest.step( + 'Prepare playback conversation with plotly graph in the response', + async () => { + const defaultModel = ModelsUtil.getDefaultModel()!; + plotlyImageUrl = await overlayFileApiHelper.putFile( + Attachment.plotlyName, + API.modelFilePath(defaultModel.id), + ); + plotlyConversation = + conversationData.prepareConversationWithAttachmentInResponse( + plotlyImageUrl, + defaultModel, + undefined, + conversationName, + ); + playbackConversation = + conversationData.prepareDefaultPlaybackConversation( + plotlyConversation, + ); + await overlayDataInjector.createConversations([ + plotlyConversation, + playbackConversation, + ]); + }, + ); + + await dialOverlayTest.step('Publish playback conversation', async () => { + const assistantMessage = plotlyConversation.messages.find( + (m) => m.role === 'assistant', + ); + let attachment; + if (assistantMessage !== undefined) { + attachment = assistantMessage.custom_content!.attachments![0]; + } + const publishRequest = publishRequestBuilder + .withName(GeneratorUtil.randomPublicationRequestName()) + .withConversationResource(playbackConversation, PublishActions.ADD) + .withFileResource(attachment!, PublishActions.ADD_IF_ABSENT) + .build(); + const publication = + await overlayPublicationApiHelper.createPublishRequest(publishRequest); + await adminPublicationApiHelper.approveRequest(publication); + publicationsToUnpublish.push(publication); + }); + + await dialOverlayTest.step( + 'Open overlay sandbox and verify playback conversation is preselected', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.overlayConversationIdSetUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayBaseAssertion.assertElementState( + overlayPlaybackControl, + 'visible', + ); + const selectedConversationIds = + await localStorageManager.getSelectedConversationIds( + process.env.NEXT_PUBLIC_OVERLAY_HOST, + ); + expect + .soft( + selectedConversationIds[0], + ExpectedMessages.conversationIsSelected, + ) + .toBe(expectedConversationId); + + //TODO: enable when fixed https://github.com/epam/ai-dial-chat/issues/2929 + // await overlayHeader.leftPanelToggle.click(); + // await overlayBaseAssertion.assertElementState( + // overlayOrganizationConversations.selectedConversation( + // ExpectedConstants.playbackConversation.concat(conversationName), + // ), + // 'visible', + // ); + // await overlayHeader.leftPanelToggle.click(); + }, + ); + + await dialOverlayTest.step( + 'Play the conversation forward and verify the graph is displayed', + async () => { + await overlayPlaybackControl.playbackNextButton.click(); + await page.keyboard.press(keys.arrowRight); + await overlayChatMessages + .getChatMessageAttachment(2, Attachment.plotlyName) + .waitForState(); + const expandAttachmentResponse = + await overlayChatMessages.expandChatMessageAttachment( + 2, + Attachment.plotlyName, + ); + expect + .soft( + expandAttachmentResponse?.status(), + ExpectedMessages.attachmentIsExpanded, + ) + .toBe(200); + await overlayBaseAssertion.assertElementState( + overlayChatMessages.getMessagePlotlyAttachment(2), + 'visible', + ); + }, + ); + + await dialOverlayTest.step( + 'Create a new conversation, refresh the page and verify playback conversation is preselected', + async () => { + await overlayHeader.createNewConversation(); + await overlayHomePage.reloadPage(); + await overlayBaseAssertion.assertElementState( + overlayPlaybackControl, + 'visible', + ); + const selectedConversationIds = + await localStorageManager.getSelectedConversationIds( + process.env.NEXT_PUBLIC_OVERLAY_HOST, + ); + expect + .soft( + selectedConversationIds[0], + ExpectedMessages.conversationIsSelected, + ) + .toBe(expectedConversationId); + + //TODO: enable when fixed https://github.com/epam/ai-dial-chat/issues/2929 + // await overlayHeader.leftPanelToggle.click(); + // await overlayBaseAssertion.assertElementState( + // overlayOrganizationConversations.selectedConversation( + // ExpectedConstants.playbackConversation.concat(conversationName), + // ), + // 'visible', + // ); + }, + ); + }, +); + +dialOverlayTest.afterAll( + async ({ overlayPublicationApiHelper, adminPublicationApiHelper }) => { + for (const publication of publicationsToUnpublish) { + const unpublishResponse = + await overlayPublicationApiHelper.createUnpublishRequest(publication); + await adminPublicationApiHelper.approveRequest(unpublishResponse); + } + }, +); diff --git a/apps/chat-e2e/src/tests/overlay/topSettingsFeature.test.ts b/apps/chat-e2e/src/tests/overlay/topSettingsFeature.test.ts new file mode 100644 index 0000000000..b3f3272dd6 --- /dev/null +++ b/apps/chat-e2e/src/tests/overlay/topSettingsFeature.test.ts @@ -0,0 +1,134 @@ +import dialTest from '@/src/core/dialFixtures'; +import dialOverlayTest from '@/src/core/dialOverlayFixtures'; +import { + ExpectedConstants, + MockedChatApiResponseBodies, + OverlaySandboxUrls, +} from '@/src/testData'; +import { GeneratorUtil } from '@/src/utils'; + +dialOverlayTest( + '[Overlay] Display clear conversations button in chat header - Feature.TopClearConversation.\n' + + '[Overlay] Display conversation info in chat header - Feature.TopChatInfo.\n' + + '[Overlay] Display change model settings button in chat header - Feature.TopChatModelSettings', + async ({ + overlayHomePage, + overlayChat, + overlayChatHeader, + overlayBaseAssertion, + setTestIds, + }) => { + setTestIds('EPMRTC-3762', 'EPMRTC-3763', 'EPMRTC-3764'); + + await dialTest.step( + 'Send the request and verify chat header is not visible', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.disableTopChatInfoUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayHomePage.mockChatTextResponse( + MockedChatApiResponseBodies.simpleTextBody, + { isOverlay: true }, + ); + await overlayChat.sendRequestWithButton(GeneratorUtil.randomString(5)); + await overlayBaseAssertion.assertElementState( + overlayChatHeader, + 'hidden', + ); + }, + ); + }, +); + +dialOverlayTest( + `[Overlay] Don't allow to click on model icon - Feature.DisallowChangeAgent`, + async ({ + overlayHomePage, + overlayChat, + overlayChatHeader, + overlayModelInfoTooltip, + overlayTalkToAgentDialog, + overlayBaseAssertion, + setTestIds, + }) => { + setTestIds('EPMRTC-4872'); + + await dialTest.step( + 'Send the request and verify chat name and icon are displayed in the header', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enableDisallowChangeAgentUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayHomePage.mockChatTextResponse( + MockedChatApiResponseBodies.simpleTextBody, + { isOverlay: true }, + ); + await overlayChat.sendRequestWithButton(GeneratorUtil.randomString(5)); + await overlayBaseAssertion.assertElementState( + overlayChatHeader.chatTitle, + 'visible', + ); + await overlayBaseAssertion.assertElementState( + overlayChatHeader.chatModelIcon, + 'visible', + ); + }, + ); + + await dialTest.step( + 'Click on model icon and verify "Select an agent" modal is not opened', + async () => { + // eslint-disable-next-line playwright/no-force-option + await overlayChatHeader.chatModelIcon.click({ force: true }); + await overlayBaseAssertion.assertElementState( + overlayTalkToAgentDialog, + 'hidden', + ); + }, + ); + + await dialTest.step( + 'Hover over model icon and verify tooltip content', + async () => { + await overlayBaseAssertion.assertElementText( + overlayModelInfoTooltip.title, + ExpectedConstants.modelInfoTooltipTitle, + ); + }, + ); + }, +); + +dialOverlayTest( + `[Overlay] Display chat menu in chat header - Feature.HideTopContextMenu`, + async ({ + overlayHomePage, + overlayChat, + overlayChatHeader, + overlayBaseAssertion, + setTestIds, + }) => { + setTestIds('EPMRTC-4873'); + + await dialTest.step( + 'Send the request and verify dots menu is not displayed in the header', + async () => { + await overlayHomePage.navigateToUrl( + OverlaySandboxUrls.enableHideTopContextMenuUrl, + ); + await overlayHomePage.waitForPageLoaded(); + await overlayHomePage.mockChatTextResponse( + MockedChatApiResponseBodies.simpleTextBody, + { isOverlay: true }, + ); + await overlayChat.sendRequestWithButton(GeneratorUtil.randomString(5)); + await overlayBaseAssertion.assertElementState( + overlayChatHeader.dotsMenu, + 'hidden', + ); + }, + ); + }, +); diff --git a/apps/chat-e2e/src/ui/pages/overlay/overlayHomePage.ts b/apps/chat-e2e/src/ui/pages/overlay/overlayHomePage.ts index 0f95530e1b..e8b9cecda7 100644 --- a/apps/chat-e2e/src/ui/pages/overlay/overlayHomePage.ts +++ b/apps/chat-e2e/src/ui/pages/overlay/overlayHomePage.ts @@ -1,3 +1,4 @@ +import { loadingTimeout } from '@/src/ui/pages'; import { OverlayBasePage } from '@/src/ui/pages/overlay/overlayBasePage'; import { AppContainer } from '@/src/ui/webElements'; import { Page } from '@playwright/test'; @@ -6,4 +7,16 @@ export class OverlayHomePage extends OverlayBasePage { constructor(page: Page) { super(page, new AppContainer(page)); } + + public async waitForPageLoaded() { + const overlayAppContainer = this.getOverlayContainer(); + await overlayAppContainer + .getChatLoader() + .waitForState({ state: 'hidden', timeout: loadingTimeout }); + const overlayChat = overlayAppContainer.getChat(); + await overlayChat.waitForState({ state: 'attached' }); + await overlayChat.waitForChatLoaded(); + await overlayChat.getSendMessage().waitForMessageInputLoaded(); + await overlayChat.getAgentInfo().waitForState({ state: 'attached' }); + } } diff --git a/apps/chat-e2e/src/ui/selectors/dialogSelectors.ts b/apps/chat-e2e/src/ui/selectors/dialogSelectors.ts index 60e0c9b5dc..20455856bd 100644 --- a/apps/chat-e2e/src/ui/selectors/dialogSelectors.ts +++ b/apps/chat-e2e/src/ui/selectors/dialogSelectors.ts @@ -65,6 +65,7 @@ export const ModelTooltip = { modelTooltip: '[data-qa="chat-model-tooltip"]', modelInfo: '[data-qa="agent-info"]', versionInfo: '[data-qa="version-info"]', + title: '[data-qa="tooltip-title"]', }; export const SettingsTooltip = { @@ -132,9 +133,9 @@ export const SelectFolderModalSelectors = { export const AccountSettingsModalSelector = { settingsModal: '[data-qa="settings-modal"]', theme: '[data-qa="theme"]', + customLogo: '[data-qa="custom-logo"]', fullWidthChatToggle: '[data-qa="toggle-switch"]', save: '[data-qa="save"]', - customLogo: '[data-qa="custom-logo"]', }; export const PublishingModalSelectors = { @@ -216,3 +217,11 @@ export const MessageTemplateModalSelectors = { showMoreButton: '[data-qa="show-more"]', showLessButton: '[data-qa="show-less"]', }; + +export const RequestApiKeyModalSelectors = { + requestApiKeyContainer: '[data-qa="request-api-key-dialog"]', +}; + +export const ReportAnIssueModalSelectors = { + reportAnIssueContainer: '[data-qa="report-issue-dialog"]', +}; diff --git a/apps/chat-e2e/src/ui/selectors/headerSelectors.ts b/apps/chat-e2e/src/ui/selectors/headerSelectors.ts index 6a7627a40a..69153a5cc1 100644 --- a/apps/chat-e2e/src/ui/selectors/headerSelectors.ts +++ b/apps/chat-e2e/src/ui/selectors/headerSelectors.ts @@ -3,8 +3,14 @@ export const HeaderSelectors = { leftPanelToggle: '[data-qa="left-panel-toggle"]:visible', rightPanelToggle: '[data-qa="right-panel-toggle"]:visible', banner: '[data-qa="banner"]', - accountSettings: '[data-qa="account-settings"]', + accountSettings: '[data-qa="account-settings"]:visible', newEntity: '[data-qa="new-entity"]', backToChatButton: '[data-qa="back-to-chat"]', - dialLogo: '[data-qa="dial-logo"]', + profilePanel: '[data-qa="profile-panel"]', + settings: '#user-settings-menu-item', + overlayLogout: '#logout-menu-item', + logo: '[data-qa="logo"]', + username: '[data-qa="username"]:visible', + avatar: '[alt="User avatar"]', + closeButton: '#close-icon', }; diff --git a/apps/chat-e2e/src/ui/selectors/marketplaceSelectors.ts b/apps/chat-e2e/src/ui/selectors/marketplaceSelectors.ts index c9ca486673..6e772936d3 100644 --- a/apps/chat-e2e/src/ui/selectors/marketplaceSelectors.ts +++ b/apps/chat-e2e/src/ui/selectors/marketplaceSelectors.ts @@ -2,6 +2,7 @@ export const marketplaceContainer = '[data-qa="marketplace"]'; export const MarketplaceSelectors = { header: '[data-qa="marketplace-header"]', + addApp: '[data-qa="add-app"]', }; export const MarketplaceAgentSelectors = { diff --git a/apps/chat-e2e/src/ui/selectors/sideBarSelectors.ts b/apps/chat-e2e/src/ui/selectors/sideBarSelectors.ts index 1ca6855617..0045187f25 100644 --- a/apps/chat-e2e/src/ui/selectors/sideBarSelectors.ts +++ b/apps/chat-e2e/src/ui/selectors/sideBarSelectors.ts @@ -25,6 +25,7 @@ export const ChatBarSelectors = { deleteConversations: '[data-qa="delete-conversations"]', compare: '[data-qa="compare"]', attachments: '[data-qa="attachments"]', + dialMarketplaceLink: '[data-qa="link-to-marketplace"]', conversations: '[data-qa="conversations"]', selectedEntity: '[data-qa="selected"]', chatFolders: '[data-qa="chat-folders"]', diff --git a/apps/chat-e2e/src/ui/webElements/accountSettings.ts b/apps/chat-e2e/src/ui/webElements/accountSettings.ts index e0f2d2e3a0..26216001fd 100644 --- a/apps/chat-e2e/src/ui/webElements/accountSettings.ts +++ b/apps/chat-e2e/src/ui/webElements/accountSettings.ts @@ -2,11 +2,11 @@ import { Attributes } from '@/src/ui/domData'; import { HeaderSelectors } from '@/src/ui/selectors'; import { BaseElement } from '@/src/ui/webElements/baseElement'; import { DropdownMenu } from '@/src/ui/webElements/dropdownMenu'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class AccountSettings extends BaseElement { - constructor(page: Page) { - super(page, HeaderSelectors.accountSettings); + constructor(page: Page, parentLocator?: Locator) { + super(page, HeaderSelectors.accountSettings, parentLocator); } private dropdownMenu!: DropdownMenu; @@ -22,6 +22,12 @@ export class AccountSettings extends BaseElement { `.${Attributes.rotated180}`, ); + public avatarIcon = this.getChildElementBySelector(HeaderSelectors.avatar); + + public closeButton = this.getChildElementBySelector( + HeaderSelectors.closeButton, + ); + public async openAccountDropdownMenu() { await this.click(); await this.getDropdownMenu().waitForState(); diff --git a/apps/chat-e2e/src/ui/webElements/appContainer.ts b/apps/chat-e2e/src/ui/webElements/appContainer.ts index 81e286218a..d8eff291f1 100644 --- a/apps/chat-e2e/src/ui/webElements/appContainer.ts +++ b/apps/chat-e2e/src/ui/webElements/appContainer.ts @@ -39,7 +39,7 @@ export class AppContainer extends BaseLayoutContainer { getPromptBar(): PromptBar { if (!this.promptBar) { - this.promptBar = new PromptBar(this.page); + this.promptBar = new PromptBar(this.page, this.rootLocator); } return this.promptBar; } diff --git a/apps/chat-e2e/src/ui/webElements/attachFilesModal.ts b/apps/chat-e2e/src/ui/webElements/attachFilesModal.ts index d0472b2335..8243481fed 100644 --- a/apps/chat-e2e/src/ui/webElements/attachFilesModal.ts +++ b/apps/chat-e2e/src/ui/webElements/attachFilesModal.ts @@ -12,7 +12,7 @@ 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'; +import { Locator, Page } from '@playwright/test'; export enum FileModalSection { AllFiles = 'All files', @@ -20,8 +20,8 @@ export enum FileModalSection { Organization = 'Organization', } export class AttachFilesModal extends BaseElement { - constructor(page: Page) { - super(page, AttachFilesModalSelectors.modalContainer); + constructor(page: Page, parentLocator?: Locator) { + super(page, AttachFilesModalSelectors.modalContainer, parentLocator); } private fileDropdownMenu!: DropdownMenu; diff --git a/apps/chat-e2e/src/ui/webElements/chat.ts b/apps/chat-e2e/src/ui/webElements/chat.ts index c43df21d74..d94a7cd96d 100644 --- a/apps/chat-e2e/src/ui/webElements/chat.ts +++ b/apps/chat-e2e/src/ui/webElements/chat.ts @@ -83,7 +83,7 @@ export class Chat extends BaseElement { getPlaybackControl(): PlaybackControl { if (!this.playbackControl) { - this.playbackControl = new PlaybackControl(this.page); + this.playbackControl = new PlaybackControl(this.page, this.rootLocator); } return this.playbackControl; } @@ -224,7 +224,7 @@ export class Chat extends BaseElement { await this.chatSpinner.waitForState({ state: 'detached' }); } - private async addModelToWorkspace() { + public async addModelToWorkspace() { if (await this.addModelButton.isVisible()) { await this.addModelButton.click(); } diff --git a/apps/chat-e2e/src/ui/webElements/chatBar.ts b/apps/chat-e2e/src/ui/webElements/chatBar.ts index 201f00cbfb..456f20751a 100644 --- a/apps/chat-e2e/src/ui/webElements/chatBar.ts +++ b/apps/chat-e2e/src/ui/webElements/chatBar.ts @@ -41,6 +41,9 @@ export class ChatBar extends SideBar { public bottomDotsMenuIcon = this.bottomPanel.getChildElementBySelector( MenuSelectors.dotsMenu, ); + public dialMarketplaceLink = this.getChildElementBySelector( + ChatBarSelectors.dialMarketplaceLink, + ); getConversationsTree(): ConversationsTree { if (!this.conversationsTree) { diff --git a/apps/chat-e2e/src/ui/webElements/chatHeader.ts b/apps/chat-e2e/src/ui/webElements/chatHeader.ts index 6297402c09..0bca8a89f0 100644 --- a/apps/chat-e2e/src/ui/webElements/chatHeader.ts +++ b/apps/chat-e2e/src/ui/webElements/chatHeader.ts @@ -1,4 +1,8 @@ -import { ChatHeaderSelectors, SideBarSelectors } from '../selectors'; +import { + ChatHeaderSelectors, + MenuSelectors, + SideBarSelectors, +} from '../selectors'; import { BaseElement } from './baseElement'; import { API } from '@/src/testData'; @@ -30,7 +34,7 @@ export class ChatHeader extends BaseElement { public deleteConversationFromComparison = this.getChildElementBySelector( ChatHeaderSelectors.deleteFromCompareIcon, ); - public openConversationSettings = this.getChildElementBySelector( + public conversationSettings = this.getChildElementBySelector( ChatHeaderSelectors.conversationSettingsIcon, ); public clearConversation = this.getChildElementBySelector( @@ -40,6 +44,7 @@ export class ChatHeader extends BaseElement { ChatHeaderSelectors.leavePlayback, ); public version = this.getChildElementBySelector(ChatHeaderSelectors.version); + public dotsMenu = this.getChildElementBySelector(MenuSelectors.dotsMenu); public async isArrowIconVisible() { return this.chatAgent @@ -58,7 +63,7 @@ export class ChatHeader extends BaseElement { async openConversationSettingsPopup() { const modelsResponsePromise = this.page.waitForResponse(API.modelsHost); const addonsResponsePromise = this.page.waitForResponse(API.addonsHost); - await this.openConversationSettings.click(); + await this.conversationSettings.click(); await modelsResponsePromise; await addonsResponsePromise; } @@ -68,6 +73,6 @@ export class ChatHeader extends BaseElement { } public async hoverOverChatSettings() { - await this.openConversationSettings.hoverOver(); + await this.conversationSettings.hoverOver(); } } diff --git a/apps/chat-e2e/src/ui/webElements/chatMessages.ts b/apps/chat-e2e/src/ui/webElements/chatMessages.ts index 329d206229..9803c68a80 100644 --- a/apps/chat-e2e/src/ui/webElements/chatMessages.ts +++ b/apps/chat-e2e/src/ui/webElements/chatMessages.ts @@ -515,6 +515,11 @@ export class ChatMessages extends BaseElement { public messageDeleteIcon = (message: string | number) => this.getChatMessage(message).locator(IconSelectors.deleteIcon); + public messageCopyIcon = (message: string | number) => + this.getChatMessage(message).locator(IconSelectors.copyIcon); + public messageRegenerateIcon = (message: string | number) => + this.getChatMessage(message).locator(ChatSelectors.regenerate); + public async openEditMessageMode(message: string | number) { const editIcon = await this.waitForEditMessageIcon(message); await editIcon.click(); diff --git a/apps/chat-e2e/src/ui/webElements/confirmationDialog.ts b/apps/chat-e2e/src/ui/webElements/confirmationDialog.ts index fee4f8b2d1..56855dd9d4 100644 --- a/apps/chat-e2e/src/ui/webElements/confirmationDialog.ts +++ b/apps/chat-e2e/src/ui/webElements/confirmationDialog.ts @@ -2,23 +2,20 @@ import { isApiStorageType } from '@/src/hooks/global-setup'; import { ShareModalSelectors } from '@/src/ui/selectors'; import { ConfirmationDialogSelectors } from '@/src/ui/selectors/dialogSelectors'; import { BaseElement } from '@/src/ui/webElements/baseElement'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class ConfirmationDialog extends BaseElement { - constructor(page: Page) { - super(page, ConfirmationDialogSelectors.container); + constructor(page: Page, parentLocator?: Locator) { + super(page, ConfirmationDialogSelectors.container, parentLocator); } - public cancelButton = new BaseElement( - this.page, + public cancelButton = this.getChildElementBySelector( ConfirmationDialogSelectors.cancelDialog, ); - public confirmButton = new BaseElement( - this.page, + public confirmButton = this.getChildElementBySelector( ConfirmationDialogSelectors.confirm, ); - public confirmMessage = new BaseElement( - this.page, + public confirmMessage = this.getChildElementBySelector( ConfirmationDialogSelectors.confirmationMessage, ); public entityName = this.getChildElementBySelector( diff --git a/apps/chat-e2e/src/ui/webElements/entityTree/sidebar/sideBarEntitiesTree.ts b/apps/chat-e2e/src/ui/webElements/entityTree/sidebar/sideBarEntitiesTree.ts index e91545a878..4db878ae26 100644 --- a/apps/chat-e2e/src/ui/webElements/entityTree/sidebar/sideBarEntitiesTree.ts +++ b/apps/chat-e2e/src/ui/webElements/entityTree/sidebar/sideBarEntitiesTree.ts @@ -67,6 +67,7 @@ export class SideBarEntitiesTree extends EntitiesTree { await this.entityDotsMenu(name, indexOrOptions).click({ force: true }); await this.getDropdownMenu().waitForState(); } + async openEditEntityNameMode(newName: string) { const input = this.getEditEntityInput(); await input.editValue(newName); diff --git a/apps/chat-e2e/src/ui/webElements/footer.ts b/apps/chat-e2e/src/ui/webElements/footer.ts index 88944fcf7c..e05ad8363e 100644 --- a/apps/chat-e2e/src/ui/webElements/footer.ts +++ b/apps/chat-e2e/src/ui/webElements/footer.ts @@ -10,7 +10,9 @@ export class Footer extends BaseElement { public async openFooterLink(linkText?: string) { linkText - ? await this.getElementLocatorByText(linkText).click() + ? await this.getChildElementBySelector(Tags.a) + .getElementLocatorByText(linkText) + .click() : await this.getChildElementBySelector(Tags.a).getNthElement(1).click(); } } diff --git a/apps/chat-e2e/src/ui/webElements/footer/reportAnIssueModal.ts b/apps/chat-e2e/src/ui/webElements/footer/reportAnIssueModal.ts new file mode 100644 index 0000000000..4fe2a1e34f --- /dev/null +++ b/apps/chat-e2e/src/ui/webElements/footer/reportAnIssueModal.ts @@ -0,0 +1,17 @@ +import { IconSelectors, ReportAnIssueModalSelectors } from '@/src/ui/selectors'; +import { BaseElement } from '@/src/ui/webElements'; +import { Locator, Page } from '@playwright/test'; + +export class ReportAnIssueModal extends BaseElement { + constructor(page: Page, parentLocator?: Locator) { + super( + page, + ReportAnIssueModalSelectors.reportAnIssueContainer, + parentLocator, + ); + } + + public cancelButton = this.getChildElementBySelector( + IconSelectors.cancelIcon, + ); +} diff --git a/apps/chat-e2e/src/ui/webElements/footer/requestApiKeyModal.ts b/apps/chat-e2e/src/ui/webElements/footer/requestApiKeyModal.ts new file mode 100644 index 0000000000..8fcb3ac2c3 --- /dev/null +++ b/apps/chat-e2e/src/ui/webElements/footer/requestApiKeyModal.ts @@ -0,0 +1,17 @@ +import { IconSelectors, RequestApiKeyModalSelectors } from '@/src/ui/selectors'; +import { BaseElement } from '@/src/ui/webElements'; +import { Locator, Page } from '@playwright/test'; + +export class RequestApiKeyModal extends BaseElement { + constructor(page: Page, parentLocator?: Locator) { + super( + page, + RequestApiKeyModalSelectors.requestApiKeyContainer, + parentLocator, + ); + } + + public cancelButton = this.getChildElementBySelector( + IconSelectors.cancelIcon, + ); +} diff --git a/apps/chat-e2e/src/ui/webElements/header.ts b/apps/chat-e2e/src/ui/webElements/header.ts index 5084877f4f..bb11006360 100644 --- a/apps/chat-e2e/src/ui/webElements/header.ts +++ b/apps/chat-e2e/src/ui/webElements/header.ts @@ -12,7 +12,7 @@ export class Header extends BaseElement { public getAccountSettings() { if (!this.accountSettings) { - this.accountSettings = new AccountSettings(this.page); + this.accountSettings = new AccountSettings(this.page, this.rootLocator); } return this.accountSettings; } @@ -32,7 +32,7 @@ export class Header extends BaseElement { HeaderSelectors.backToChatButton, ); - public dialLogo = this.getChildElementBySelector(HeaderSelectors.dialLogo); + public logo = this.getChildElementBySelector(HeaderSelectors.logo); public async createNewConversation() { await this.newEntityButton.click(); diff --git a/apps/chat-e2e/src/ui/webElements/marketplace/marketplaceHeader.ts b/apps/chat-e2e/src/ui/webElements/marketplace/marketplaceHeader.ts index 816085e968..15d4bf85c7 100644 --- a/apps/chat-e2e/src/ui/webElements/marketplace/marketplaceHeader.ts +++ b/apps/chat-e2e/src/ui/webElements/marketplace/marketplaceHeader.ts @@ -13,4 +13,7 @@ export class MarketplaceHeader extends BaseElement { public searchInput = this.getChildElementBySelector( MarketplaceSideBarSelectors.searchInput, ); + public addAppButton = this.getChildElementBySelector( + MarketplaceSelectors.addApp, + ); } diff --git a/apps/chat-e2e/src/ui/webElements/menu.ts b/apps/chat-e2e/src/ui/webElements/menu.ts index 5024fb9d2b..e2249d399c 100644 --- a/apps/chat-e2e/src/ui/webElements/menu.ts +++ b/apps/chat-e2e/src/ui/webElements/menu.ts @@ -4,8 +4,8 @@ import { Locator, Page } from '@playwright/test'; import { Response } from 'playwright-core'; export abstract class Menu extends BaseElement { - constructor(page: Page) { - super(page, MenuSelectors.dropdownMenu); + constructor(page: Page, parentLocator?: Locator) { + super(page, MenuSelectors.dropdownMenu, parentLocator); } abstract menuOptions(): BaseElement; diff --git a/apps/chat-e2e/src/ui/webElements/modelInfoTooltip.ts b/apps/chat-e2e/src/ui/webElements/modelInfoTooltip.ts index 9e8d0ec7fd..af80ab4137 100644 --- a/apps/chat-e2e/src/ui/webElements/modelInfoTooltip.ts +++ b/apps/chat-e2e/src/ui/webElements/modelInfoTooltip.ts @@ -1,12 +1,13 @@ import { ModelTooltip } from '@/src/ui/selectors/dialogSelectors'; import { BaseElement } from '@/src/ui/webElements/baseElement'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class ModelInfoTooltip extends BaseElement { - constructor(page: Page) { - super(page, ModelTooltip.modelTooltip); + constructor(page: Page, parentLocator?: Locator) { + super(page, ModelTooltip.modelTooltip, parentLocator); } + public title = this.getChildElementBySelector(ModelTooltip.title); public modelInfo = this.getChildElementBySelector(ModelTooltip.modelInfo); public versionInfo = this.getChildElementBySelector(ModelTooltip.versionInfo); diff --git a/apps/chat-e2e/src/ui/webElements/overlay/profilePanel.ts b/apps/chat-e2e/src/ui/webElements/overlay/profilePanel.ts new file mode 100644 index 0000000000..1ae52e9ff9 --- /dev/null +++ b/apps/chat-e2e/src/ui/webElements/overlay/profilePanel.ts @@ -0,0 +1,24 @@ +import { HeaderSelectors } from '@/src/ui/selectors'; +import { BaseElement } from '@/src/ui/webElements'; +import { Footer } from '@/src/ui/webElements/footer'; +import { Locator, Page } from '@playwright/test'; + +export class ProfilePanel extends BaseElement { + constructor(page: Page, parentLocator?: Locator) { + super(page, HeaderSelectors.profilePanel, parentLocator); + } + + public settings = this.getChildElementBySelector(HeaderSelectors.settings); + public logout = this.getChildElementBySelector(HeaderSelectors.overlayLogout); + public username = this.getChildElementBySelector(HeaderSelectors.username); + public avatar = this.getChildElementBySelector(HeaderSelectors.avatar); + + private footer!: Footer; + + getFooter(): Footer { + if (!this.footer) { + this.footer = new Footer(this.page, this.rootLocator); + } + return this.footer; + } +} diff --git a/apps/chat-e2e/src/ui/webElements/playbackControl.ts b/apps/chat-e2e/src/ui/webElements/playbackControl.ts index ca8c6db27e..b46567b7eb 100644 --- a/apps/chat-e2e/src/ui/webElements/playbackControl.ts +++ b/apps/chat-e2e/src/ui/webElements/playbackControl.ts @@ -1,11 +1,11 @@ import { PlaybackSelectors } from '@/src/ui/selectors'; import { BaseElement } from '@/src/ui/webElements/baseElement'; import { PlaybackMessage } from '@/src/ui/webElements/playbackMessage'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class PlaybackControl extends BaseElement { - constructor(page: Page) { - super(page, PlaybackSelectors.playbackControl); + constructor(page: Page, parentLocator: Locator) { + super(page, PlaybackSelectors.playbackControl, parentLocator); } private playbackMessage!: PlaybackMessage; diff --git a/apps/chat-e2e/src/ui/webElements/promptBar.ts b/apps/chat-e2e/src/ui/webElements/promptBar.ts index 874a33174d..c160c2e10a 100644 --- a/apps/chat-e2e/src/ui/webElements/promptBar.ts +++ b/apps/chat-e2e/src/ui/webElements/promptBar.ts @@ -10,11 +10,11 @@ import { } from '@/src/ui/webElements/entityTree'; import { OrganizationPromptsTree } from '@/src/ui/webElements/entityTree/sidebar/organizationPromptsTree'; import { SideBar } from '@/src/ui/webElements/sideBar'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class PromptBar extends SideBar { - constructor(page: Page) { - super(page, SideBarSelectors.promptBar); + constructor(page: Page, parentLocator: Locator) { + super(page, SideBarSelectors.promptBar, parentLocator); } private promptsTree!: PromptsTree; diff --git a/apps/chat-e2e/src/ui/webElements/publishingRequestModal.ts b/apps/chat-e2e/src/ui/webElements/publishingRequestModal.ts index ce44542709..4b1605c654 100644 --- a/apps/chat-e2e/src/ui/webElements/publishingRequestModal.ts +++ b/apps/chat-e2e/src/ui/webElements/publishingRequestModal.ts @@ -1,6 +1,7 @@ import { Publication, PublicationRequestModel } from '@/chat/types/publication'; import { API } from '@/src/testData'; import { + IconSelectors, PublishingApprovalModalSelectors, PublishingModalSelectors, } from '@/src/ui/selectors'; @@ -15,11 +16,11 @@ import { PromptsToPublishTree, } from '@/src/ui/webElements/entityTree'; import { PublishingRules } from '@/src/ui/webElements/publishingRules'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class PublishingRequestModal extends BaseElement { - constructor(page: Page) { - super(page, PublishingModalSelectors.modalContainer); + constructor(page: Page, parentLocator?: Locator) { + super(page, PublishingModalSelectors.modalContainer, parentLocator); } public allowAccessLabel = this.getChildElementBySelector( @@ -28,6 +29,9 @@ export class PublishingRequestModal extends BaseElement { public availabilityLabel = this.getChildElementBySelector( PublishingApprovalModalSelectors.availabilityLabel, ); + public cancelButton = this.getChildElementBySelector( + IconSelectors.cancelIcon, + ); //conversations to publish trees private conversationsToPublishTree!: ConversationsToPublishTree; diff --git a/apps/chat-e2e/src/ui/webElements/settingsModal.ts b/apps/chat-e2e/src/ui/webElements/settingsModal.ts index fa9ef520fa..7056037cd6 100644 --- a/apps/chat-e2e/src/ui/webElements/settingsModal.ts +++ b/apps/chat-e2e/src/ui/webElements/settingsModal.ts @@ -1,12 +1,13 @@ import { Styles, Tags } from '@/src/ui/domData'; +import { IconSelectors } from '@/src/ui/selectors'; import { AccountSettingsModalSelector } from '@/src/ui/selectors/dialogSelectors'; import { BaseElement } from '@/src/ui/webElements/baseElement'; import { DropdownButtonMenu } from '@/src/ui/webElements/dropdownButtonMenu'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class SettingsModal extends BaseElement { - constructor(page: Page) { - super(page, AccountSettingsModalSelector.settingsModal); + constructor(page: Page, parentLocator?: Locator) { + super(page, AccountSettingsModalSelector.settingsModal, parentLocator); } private themeDropdownMenu!: DropdownButtonMenu; @@ -21,18 +22,20 @@ export class SettingsModal extends BaseElement { public theme = this.getChildElementBySelector( AccountSettingsModalSelector.theme, ); + public customLogo = this.getChildElementBySelector( + AccountSettingsModalSelector.customLogo, + ); public fullWidthChatToggle = this.getChildElementBySelector( AccountSettingsModalSelector.fullWidthChatToggle, ); - public customLogo = this.getChildElementBySelector( - AccountSettingsModalSelector.customLogo, - ); - public saveButton = this.getChildElementBySelector( AccountSettingsModalSelector.save, ); + public cancelButton = this.getChildElementBySelector( + IconSelectors.cancelIcon, + ); public async getFullWidthChatToggleColor() { const toggleColor = await this.fullWidthChatToggle diff --git a/apps/chat-e2e/src/ui/webElements/shareModal.ts b/apps/chat-e2e/src/ui/webElements/shareModal.ts index 212545b566..a3f4b41e5b 100644 --- a/apps/chat-e2e/src/ui/webElements/shareModal.ts +++ b/apps/chat-e2e/src/ui/webElements/shareModal.ts @@ -3,11 +3,11 @@ import { BaseElement } from './baseElement'; import { Tags } from '@/src/ui/domData'; import { ChatSelectors, ShareModalSelectors } from '@/src/ui/selectors'; import { IconSelectors } from '@/src/ui/selectors/iconSelectors'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class ShareModal extends BaseElement { - constructor(page: Page) { - super(page, ShareModalSelectors.modalContainer); + constructor(page: Page, parentLocator?: Locator) { + super(page, ShareModalSelectors.modalContainer, parentLocator); } public closeButton = this.getChildElementBySelector(IconSelectors.cancelIcon); diff --git a/apps/chat-e2e/src/ui/webElements/talkToAgentDialog.ts b/apps/chat-e2e/src/ui/webElements/talkToAgentDialog.ts index 2054f1df71..c67b8eebc3 100644 --- a/apps/chat-e2e/src/ui/webElements/talkToAgentDialog.ts +++ b/apps/chat-e2e/src/ui/webElements/talkToAgentDialog.ts @@ -23,6 +23,9 @@ export class TalkToAgentDialog extends BaseElement { public goToMyWorkspaceButton = this.getChildElementBySelector( TalkToAgentDialogSelectors.goToMyWorkspaceButton, ); + public searchAgentInput = this.getChildElementBySelector( + TalkToAgentDialogSelectors.searchAgent, + ); public cancelButton = this.getChildElementBySelector( IconSelectors.cancelIcon, ); diff --git a/apps/chat-e2e/src/ui/webElements/toast.ts b/apps/chat-e2e/src/ui/webElements/toast.ts index c139356a65..0505827a39 100644 --- a/apps/chat-e2e/src/ui/webElements/toast.ts +++ b/apps/chat-e2e/src/ui/webElements/toast.ts @@ -1,11 +1,11 @@ import { Tags } from '@/src/ui/domData'; import { ToastSelectors } from '@/src/ui/selectors'; import { BaseElement } from '@/src/ui/webElements/baseElement'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class Toast extends BaseElement { - constructor(page: Page) { - super(page, ToastSelectors.toast); + constructor(page: Page, parentLocator?: Locator) { + super(page, ToastSelectors.toast, parentLocator); } public closeButton = this.getChildElementBySelector(Tags.button); diff --git a/apps/chat/src/components/Chat/ChatHeader/HeaderModelTooltip.tsx b/apps/chat/src/components/Chat/ChatHeader/HeaderModelTooltip.tsx index cc4f80928f..15bbbb66ed 100644 --- a/apps/chat/src/components/Chat/ChatHeader/HeaderModelTooltip.tsx +++ b/apps/chat/src/components/Chat/ChatHeader/HeaderModelTooltip.tsx @@ -23,7 +23,7 @@ export const HeaderModelTooltip = ({ className="grid max-w-[880px] grid-cols-1 p-2" data-qa="chat-model-tooltip" > -
+
{t(disallowChangeAgent ? 'Current agent' : 'Change current agent')}:
diff --git a/apps/chat/src/components/Chat/ReportIssueDialog.tsx b/apps/chat/src/components/Chat/ReportIssueDialog.tsx index 08ea2a014c..27364dd815 100644 --- a/apps/chat/src/components/Chat/ReportIssueDialog.tsx +++ b/apps/chat/src/components/Chat/ReportIssueDialog.tsx @@ -104,7 +104,7 @@ export const ReportIssueDialog: FC = ({ isOpen, onClose }) => { portalId="theme-main" state={isOpen ? ModalState.OPENED : ModalState.CLOSED} onClose={handleClose} - dataQa="request-api-key-dialog" + dataQa="report-issue-dialog" overlayClassName="fixed inset-0" containerClassName="inline-block w-full overflow-y-auto px-3 py-4 align-bottom transition-all md:p-6 xl:max-h-[800px] xl:max-w-[720px] 2xl:max-w-[780px]" form={{ diff --git a/apps/chat/src/components/Header/Logo.tsx b/apps/chat/src/components/Header/Logo.tsx index dd9c374356..8cca2eb773 100644 --- a/apps/chat/src/components/Header/Logo.tsx +++ b/apps/chat/src/components/Header/Logo.tsx @@ -73,12 +73,12 @@ export const Logo = () => { 'mx-auto min-w-[110px] bg-contain bg-center bg-no-repeat md:ml-5 lg:bg-left', messageIsStreaming && 'cursor-not-allowed', )} - data-qa="dial-logo" style={{ backgroundImage: customLogoUrl ? `url(${cssEscape(customLogoUrl)})` : `var(--app-logo)`, }} + data-qa="logo" > ); }; diff --git a/apps/chat/src/components/Header/User/ProfileButton.tsx b/apps/chat/src/components/Header/User/ProfileButton.tsx index 3321e3f905..fa3359fe8f 100644 --- a/apps/chat/src/components/Header/User/ProfileButton.tsx +++ b/apps/chat/src/components/Header/User/ProfileButton.tsx @@ -39,9 +39,15 @@ export const ProfileButton = () => {
{ } > @@ -81,7 +83,7 @@ export const UserDesktop = Inversify.register('UserDesktop', () => { }} /> diff --git a/apps/chat/src/components/Header/User/UserMobile.tsx b/apps/chat/src/components/Header/User/UserMobile.tsx index 4191cfe1c3..cf15b03d22 100644 --- a/apps/chat/src/components/Header/User/UserMobile.tsx +++ b/apps/chat/src/components/Header/User/UserMobile.tsx @@ -43,7 +43,9 @@ const UserInfo = () => { )} - {session?.user?.name ?? ''} + + {session?.user?.name ?? ''} +
); @@ -59,7 +61,7 @@ const UserSettings = () => { return (
@@ -81,7 +83,7 @@ const Logout = () => { return ( <>
{ if (!session) { @@ -128,6 +130,7 @@ export const UserMobile = Inversify.register('UserMobile', () => { 'fixed right-0 z-40 flex w-[260px] flex-col overflow-y-auto border-tertiary bg-layer-3 md:hidden', isOverlay ? 'top-9 h-[calc(100%-36px)]' : 'top-12 h-[calc(100%-48px)]', )} + data-qa="profile-panel" > diff --git a/apps/chat/src/components/Marketplace/SearchHeader.tsx b/apps/chat/src/components/Marketplace/SearchHeader.tsx index 1852fa8e5c..631dbe8e22 100644 --- a/apps/chat/src/components/Marketplace/SearchHeader.tsx +++ b/apps/chat/src/components/Marketplace/SearchHeader.tsx @@ -61,7 +61,10 @@ const AddAppButton = ({ menuItems }: AddAppButtonProps) => { onOpenChange={setIsOpen} placement="bottom" TriggerCustomRenderer={ -
{t('Settings')}
; } diff --git a/apps/overlay-sandbox/app/cases/overlay-manager/page.tsx b/apps/overlay-sandbox/app/cases/overlay-manager/page.tsx index 7cfe822e3c..0286d656da 100644 --- a/apps/overlay-sandbox/app/cases/overlay-manager/page.tsx +++ b/apps/overlay-sandbox/app/cases/overlay-manager/page.tsx @@ -1,10 +1,11 @@ import { ChatOverlayManagerWrapper } from '../components/chatOverlayManagerWrapper'; +import { commonOverlayProps } from '../components/chatOverlayWrapper'; import { ChatOverlayManagerOptions, Feature } from '@epam/ai-dial-overlay'; const overlayOptions: Omit = { id: 'test', - domain: process.env.NEXT_PUBLIC_OVERLAY_HOST!, + ...commonOverlayProps, theme: 'light', modelId: 'gpt-4', enabledFeatures: [ @@ -21,11 +22,6 @@ const overlayOptions: Omit = { Feature.ReportAnIssue, Feature.Likes, ], - requestTimeout: 20000, - loaderStyles: { - background: 'white', - fontSize: '24px', - }, allowFullscreen: true, iconSvg: ` diff --git a/apps/overlay-sandbox/app/cases/overlay/disabled-all-features-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/disabled-all-features-sandbox/page.tsx new file mode 100644 index 0000000000..7634153f5a --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/disabled-all-features-sandbox/page.tsx @@ -0,0 +1,15 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/disabled-header-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/disabled-header-sandbox/page.tsx new file mode 100644 index 0000000000..95966ae0d2 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/disabled-header-sandbox/page.tsx @@ -0,0 +1,38 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + theme: 'light', + enabledFeatures: [ + Feature.ConversationsSection, + Feature.PromptsSection, + Feature.TopSettings, + Feature.TopClearConversation, + Feature.TopChatInfo, + Feature.TopChatModelSettings, + Feature.EmptyChatSettings, + Feature.RequestApiKey, + Feature.ReportAnIssue, + Feature.Likes, + Feature.Marketplace, + Feature.HideNewConversation, + Feature.ConversationsSharing, + Feature.PromptsSharing, + Feature.AttachmentsManager, + Feature.ConversationsPublishing, + Feature.PromptsPublishing, + Feature.CustomLogo, + Feature.Footer, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/disabled-marketplace-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/disabled-marketplace-sandbox/page.tsx new file mode 100644 index 0000000000..a3c0d13eb2 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/disabled-marketplace-sandbox/page.tsx @@ -0,0 +1,23 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + modelId: 'gpt-4', + enabledFeatures: [ + Feature.ConversationsSection, + Feature.Header, + Feature.TopSettings, + Feature.TopChatInfo, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/disabled-top-chat-info-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/disabled-top-chat-info-sandbox/page.tsx new file mode 100644 index 0000000000..3da2475712 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/disabled-top-chat-info-sandbox/page.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [ + Feature.TopClearConversation, + Feature.TopChatInfo, + Feature.TopChatModelSettings, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-disallow-change-agent-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-disallow-change-agent-sandbox/page.tsx new file mode 100644 index 0000000000..41003f2620 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-disallow-change-agent-sandbox/page.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [ + Feature.TopSettings, + Feature.TopChatInfo, + Feature.DisallowChangeAgent, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-empty-chat-settings-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-empty-chat-settings-sandbox/page.tsx new file mode 100644 index 0000000000..22584bd747 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-empty-chat-settings-sandbox/page.tsx @@ -0,0 +1,23 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [ + Feature.EmptyChatSettings, + Feature.Header, + Feature.ConversationsSection, + Feature.TopSettings, + Feature.TopClearConversation, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-header-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-header-sandbox/page.tsx new file mode 100644 index 0000000000..c4f26679b9 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-header-sandbox/page.tsx @@ -0,0 +1,39 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [ + Feature.Header, + Feature.Footer, + Feature.ConversationsSection, + Feature.PromptsSection, + Feature.TopSettings, + Feature.TopClearConversation, + Feature.TopChatInfo, + Feature.TopChatModelSettings, + Feature.EmptyChatSettings, + Feature.RequestApiKey, + Feature.ReportAnIssue, + Feature.Likes, + Feature.Marketplace, + Feature.HideNewConversation, + Feature.ConversationsSharing, + Feature.PromptsSharing, + Feature.AttachmentsManager, + Feature.ConversationsPublishing, + Feature.PromptsPublishing, + Feature.CustomLogo, + Feature.MessageTemplates, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-hide-empty-change-agent-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-hide-empty-change-agent-sandbox/page.tsx new file mode 100644 index 0000000000..48f1128c3a --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-hide-empty-change-agent-sandbox/page.tsx @@ -0,0 +1,17 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [Feature.HideEmptyChatChangeAgent], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-hide-top-context-menu-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-hide-top-context-menu-sandbox/page.tsx new file mode 100644 index 0000000000..00022292c8 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-hide-top-context-menu-sandbox/page.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [ + Feature.TopSettings, + Feature.ConversationsSection, + Feature.HideTopContextMenu, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-input-files-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-input-files-sandbox/page.tsx new file mode 100644 index 0000000000..d3e4d3bc23 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-input-files-sandbox/page.tsx @@ -0,0 +1,22 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [ + Feature.EmptyChatSettings, + Feature.Header, + Feature.ConversationsSection, + Feature.InputFiles, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-marketplace-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-marketplace-sandbox/page.tsx new file mode 100644 index 0000000000..b7ca9e79c4 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-marketplace-sandbox/page.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + modelId: 'gpt-4', + enabledFeatures: [ + Feature.ConversationsSection, + Feature.Header, + Feature.TopSettings, + Feature.TopChatInfo, + Feature.Marketplace, + Feature.CodeApps, + Feature.QuickApps, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-only-footer-links-attachments-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-only-footer-links-attachments-sandbox/page.tsx new file mode 100644 index 0000000000..bedd9f5f22 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-only-footer-links-attachments-sandbox/page.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [ + Feature.ReportAnIssue, + Feature.RequestApiKey, + Feature.AttachmentsManager, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-conversations-section-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-conversations-section-sandbox/page.tsx new file mode 100644 index 0000000000..c331e825a8 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-conversations-section-sandbox/page.tsx @@ -0,0 +1,17 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [Feature.Header, Feature.ConversationsSection], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-footer-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-footer-sandbox/page.tsx new file mode 100644 index 0000000000..cd9bdd7515 --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-footer-sandbox/page.tsx @@ -0,0 +1,17 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [Feature.Header, Feature.Footer], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-sandbox/page.tsx new file mode 100644 index 0000000000..94ef790b8f --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/enabled-only-header-sandbox/page.tsx @@ -0,0 +1,17 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + enabledFeatures: [Feature.Header], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/cases/overlay/model-id-set-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/model-id-set-sandbox/page.tsx index f1eb734079..4adcf76327 100644 --- a/apps/overlay-sandbox/app/cases/overlay/model-id-set-sandbox/page.tsx +++ b/apps/overlay-sandbox/app/cases/overlay/model-id-set-sandbox/page.tsx @@ -1,12 +1,14 @@ 'use client'; -import { ChatOverlayWrapper } from '../../components/chatOverlayWrapper'; +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; import { Feature } from '@epam/ai-dial-shared'; const overlayOptions = { - domain: process.env.NEXT_PUBLIC_OVERLAY_HOST!, - theme: 'light', + ...commonOverlayProps, modelId: 'gpt-4', enabledFeatures: [ Feature.ConversationsSection, @@ -23,11 +25,6 @@ const overlayOptions = { Feature.Likes, Feature.Marketplace, ], - requestTimeout: 20000, - loaderStyles: { - background: 'white', - fontSize: '24px', - }, }; export default function Index() { diff --git a/apps/overlay-sandbox/app/cases/overlay/overlay-conversation-id-set-sandbox/page.tsx b/apps/overlay-sandbox/app/cases/overlay/overlay-conversation-id-set-sandbox/page.tsx new file mode 100644 index 0000000000..9b102bf0aa --- /dev/null +++ b/apps/overlay-sandbox/app/cases/overlay/overlay-conversation-id-set-sandbox/page.tsx @@ -0,0 +1,23 @@ +'use client'; + +import { + ChatOverlayWrapper, + commonOverlayProps, +} from '../../components/chatOverlayWrapper'; + +import { Feature } from '@epam/ai-dial-shared'; + +const overlayOptions = { + ...commonOverlayProps, + overlayConversationId: + 'conversations/public/playback__[Playback] overlayConversationName__0.0.1', + enabledFeatures: [ + Feature.ConversationsSection, + Feature.ConversationsPublishing, + Feature.Header, + ], +}; + +export default function Index() { + return ; +} diff --git a/apps/overlay-sandbox/app/global.css b/apps/overlay-sandbox/app/global.css index b5c61c9567..dbeeb253f5 100644 --- a/apps/overlay-sandbox/app/global.css +++ b/apps/overlay-sandbox/app/global.css @@ -1,3 +1,15 @@ @tailwind base; @tailwind components; @tailwind utilities; + +.link-container { + display: flex; + flex-wrap: wrap; + gap: 1rem; +} + +@media (max-width: 768px) { + .link-container { + flex-direction: column; + } +} diff --git a/apps/overlay-sandbox/app/page.tsx b/apps/overlay-sandbox/app/page.tsx index c7ef2254c7..18e7401896 100644 --- a/apps/overlay-sandbox/app/page.tsx +++ b/apps/overlay-sandbox/app/page.tsx @@ -3,8 +3,104 @@ import Link from 'next/link'; export default async function Index() { return (
- Overlay - Overlay Manager +
+ + + modelIdSetSandboxOverlay + + + + OverlayManager + + + + disabledHeaderOverlay + + + + + enabledHeaderOverlay + + + + + enabledOnlyHeaderOverlay + + + + + enabledOnlyHeaderFooterOverlay + + + + + enabledOnlyFooterLinksAttachmentsOverlay + + + + + enabledOnlyHeaderConversationsSectionOverlay + + + + + disableTopChatInfoOverlay + + + + + enableDisallowChangeAgentOverlay + + + + + enableHideTopContextMenuOverlay + + + + + enableEmptyChatSettingsOverlay + + + + + enableInputFilesOverlay + + + + + enableHideEmptyChangeAgentOverlay + + + + + disableAllFeaturesOverlay + + + + + enableMarketplaceOverlay + + + + + disableMarketplaceOverlay + + + + + conversationIdSetOverlay + + +
); } diff --git a/apps/overlay-sandbox/next.config.js b/apps/overlay-sandbox/next.config.js index 4f7ce385db..f236a05a0a 100644 --- a/apps/overlay-sandbox/next.config.js +++ b/apps/overlay-sandbox/next.config.js @@ -20,6 +20,9 @@ const nextConfig = { return config; }, + experimental: { + workerThreads: true + } }; const plugins = [