From 57c07ed874fd38ffc03f02c3033d38c4cb8517f4 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 4 Sep 2025 13:08:44 -0600 Subject: [PATCH 1/8] rm iconColor --- .../conversation_sidepanel/conversation_list_item.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx index ab769a69b7813..706434d755ef2 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx @@ -67,17 +67,15 @@ export const ConversationListItem: React.FC = ({ () => isAssistantSharingEnabled && conversationSharedState !== ConversationSharedState.PRIVATE, [isAssistantSharingEnabled, conversationSharedState] ); - const { iconType, iconColor, iconTitle } = useMemo( + const { iconType, iconTitle } = useMemo( () => conversation.isConversationOwner ? { iconType: shouldShowIcon ? getSharedIcon(conversationSharedState) : undefined, - iconColor: 'accent', iconTitle: i18n.SHARED_BY_YOU, } : { iconType: shouldShowIcon ? getSharedIcon(conversationSharedState) : undefined, - iconColor: 'default', iconTitle: i18n.SHARED_WITH_YOU, }, [conversation.isConversationOwner, shouldShowIcon, conversationSharedState] @@ -151,7 +149,6 @@ export const ConversationListItem: React.FC = ({ margin-inline-start: 12px; margin-inline-end: 0px; `, - color: iconColor, }} data-test-subj={`conversation-select-${conversation.title}`} isActive={isActiveConversation} From f998f19f32922914db1aaaffdb8616effaa5ea47 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 4 Sep 2025 13:13:55 -0600 Subject: [PATCH 2/8] rm feature flag --- api_docs/kbn_elastic_assistant.devdocs.json | 30 +------------------ .../assistant/assistant_header/index.test.tsx | 15 +++------- .../impl/assistant/assistant_header/index.tsx | 4 +-- .../conversation_list_item.test.tsx | 3 +- .../conversation_list_item.tsx | 25 ++-------------- .../conversation_sidepanel/index.tsx | 4 --- .../impl/assistant/index.tsx | 4 +-- .../impl/assistant_context/types.tsx | 3 -- .../shared/kbn-elastic-assistant/index.ts | 3 -- .../use_assistant_availability.ts | 6 ---- .../test/security_solution_cypress/config.ts | 1 - 11 files changed, 11 insertions(+), 87 deletions(-) diff --git a/api_docs/kbn_elastic_assistant.devdocs.json b/api_docs/kbn_elastic_assistant.devdocs.json index 421283b81de66..26d129a3bad12 100644 --- a/api_docs/kbn_elastic_assistant.devdocs.json +++ b/api_docs/kbn_elastic_assistant.devdocs.json @@ -1484,20 +1484,6 @@ "path": "x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/elastic-assistant", - "id": "def-public.AssistantAvailability.isAssistantSharingEnabled", - "type": "CompoundType", - "tags": [], - "label": "isAssistantSharingEnabled", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -2796,20 +2782,6 @@ "path": "x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/elastic-assistant", - "id": "def-public.UseAssistantAvailability.isAssistantSharingEnabled", - "type": "CompoundType", - "tags": [], - "label": "isAssistantSharingEnabled", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -3240,4 +3212,4 @@ "misc": [], "objects": [] } -} \ No newline at end of file +} diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx index f3b084695879f..d4b74d8c9268c 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx @@ -109,7 +109,7 @@ describe('AssistantHeader', () => { }); it('renders share badge when sharing is enabled', () => { - render(, { + render(, { wrapper: TestProviders, }); expect(screen.getByTestId('shareBadgeButton')).not.toBeDisabled(); @@ -120,16 +120,9 @@ describe('AssistantHeader', () => { }); it('disables share badge when isConversationOwner=false', () => { - render( - , - { - wrapper: TestProviders, - } - ); + render(, { + wrapper: TestProviders, + }); expect(screen.getByTestId('shareBadgeButton')).toBeDisabled(); expect(screen.getByTestId('connector-selector')).toBeDisabled(); expect( diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx index 58426557cf659..7ef4890c1dc3a 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx @@ -44,7 +44,6 @@ interface OwnProps { isConversationOwner: boolean; isDisabled: boolean; isLoading: boolean; - isAssistantSharingEnabled?: boolean; isSettingsModalVisible: boolean; setIsSettingsModalVisible: React.Dispatch>; onChatCleared: () => void; @@ -91,7 +90,6 @@ export const AssistantHeader: React.FC = ({ defaultConnector, isConversationOwner, isAssistantEnabled, - isAssistantSharingEnabled = false, isDisabled, isLoading, isSettingsModalVisible, @@ -223,7 +221,7 @@ export const AssistantHeader: React.FC = ({ )} - {!isNewConversation && isAssistantSharingEnabled && ( + {!isNewConversation && ( { }); it('renders extraAction when sharing is disabled', () => { - render(); + render(); // The extraAction is a delete button expect(screen.getByTestId('delete-option')).toBeInTheDocument(); fireEvent.click(screen.getByTestId('delete-option')); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx index 706434d755ef2..94f13c59540df 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.tsx @@ -7,7 +7,6 @@ import React, { useCallback, useMemo } from 'react'; import { css } from '@emotion/react'; -import type { EuiListGroupItemExtraActionProps } from '@elastic/eui'; import { EuiFlexGroup, EuiListGroupItem } from '@elastic/eui'; import { ConversationSharedState, getConversationSharedState } from '@kbn/elastic-assistant-common'; import { getSharedIcon } from '../../share_conversation/utils'; @@ -22,7 +21,6 @@ interface Props { conversation: ConversationWithOwner; handleCopyUrl: (conversation: Conversation) => Promise; handleDuplicateConversation: (conversation: Conversation) => Promise; - isAssistantSharingEnabled?: boolean; isActiveConversation: boolean; lastConversationId: string; onConversationSelected: ({ cId }: { cId: string }) => void; @@ -34,7 +32,6 @@ export const ConversationListItem: React.FC = ({ conversation, handleCopyUrl, handleDuplicateConversation, - isAssistantSharingEnabled = false, isActiveConversation, lastConversationId, onConversationSelected, @@ -64,8 +61,8 @@ export const ConversationListItem: React.FC = ({ ); const shouldShowIcon = useMemo( - () => isAssistantSharingEnabled && conversationSharedState !== ConversationSharedState.PRIVATE, - [isAssistantSharingEnabled, conversationSharedState] + () => conversationSharedState !== ConversationSharedState.PRIVATE, + [conversationSharedState] ); const { iconType, iconTitle } = useMemo( () => @@ -109,21 +106,6 @@ export const ConversationListItem: React.FC = ({ [handleCopyUrl, handleDuplicateConversation, setDeleteConversationItem, conversation] ); - const extraAction = useMemo( - () => - isAssistantSharingEnabled - ? undefined - : { - color: 'danger', - onClick: () => setDeleteConversationItem(conversation), - iconType: 'trash', - iconSize: 's', - 'aria-label': i18n.DELETE_CONVERSATION, - 'data-test-subj': 'delete-option', - }, - [conversation, isAssistantSharingEnabled, setDeleteConversationItem] - ); - return ( @@ -152,9 +134,8 @@ export const ConversationListItem: React.FC = ({ }} data-test-subj={`conversation-select-${conversation.title}`} isActive={isActiveConversation} - extraAction={extraAction} /> - {isAssistantSharingEnabled && } + {/* Observer element for infinite scrolling pagination of conversations */} {conversation.id === lastConversationId && ( diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/index.tsx index 2a83a7eafbb2f..2c40facb34f9d 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/index.tsx @@ -36,7 +36,6 @@ interface Props { currentConversation?: Conversation; onConversationSelected: ({ cId }: { cId: string }) => void; shouldDisableKeyboardShortcut?: () => boolean; - isAssistantSharingEnabled?: boolean; isDisabled?: boolean; isFetchingCurrentUserConversations: boolean; conversations: Record; @@ -85,7 +84,6 @@ export const ConversationSidePanel = React.memo( currentConversation, onConversationSelected, shouldDisableKeyboardShortcut = () => false, - isAssistantSharingEnabled, isDisabled = false, isFetchingCurrentUserConversations, conversations, @@ -202,7 +200,6 @@ export const ConversationSidePanel = React.memo( : conversation.title === currentConversation?.title } key={conversation.id} - isAssistantSharingEnabled={isAssistantSharingEnabled} handleDuplicateConversation={handleDuplicateConversation} handleCopyUrl={handleCopyUrl} lastConversationId={lastConversationId} @@ -222,7 +219,6 @@ export const ConversationSidePanel = React.memo( currentConversation?.title, handleCopyUrl, handleDuplicateConversation, - isAssistantSharingEnabled, lastConversationId, onConversationSelected, setPaginationObserver, diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx index 6550a9ee92b73..63f9d70b6d3ff 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx @@ -79,7 +79,7 @@ const AssistantComponent: React.FC = ({ }) => { const { euiTheme } = useEuiTheme(); const { - assistantAvailability: { isAssistantEnabled, isAssistantSharingEnabled }, + assistantAvailability: { isAssistantEnabled }, assistantTelemetry, currentAppId, augmentMessageCodeBlocks, @@ -445,7 +445,6 @@ const AssistantComponent: React.FC = ({ conversations={conversations} onConversationDeleted={handleOnConversationDeleted} onConversationCreate={handleCreateConversation} - isAssistantSharingEnabled={isAssistantSharingEnabled} isFetchingCurrentUserConversations={isFetchingCurrentUserConversations} refetchCurrentUserConversations={refetchCurrentUserConversations} setPaginationObserver={setPaginationObserver} @@ -477,7 +476,6 @@ const AssistantComponent: React.FC = ({ conversationSharedState={conversationSharedState} defaultConnector={defaultConnector} isAssistantEnabled={isAssistantEnabled} - isAssistantSharingEnabled={isAssistantSharingEnabled} isConversationOwner={isConversationOwner} isDisabled={isDisabled || isLoadingChatSend} isLoading={isInitialLoad} diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx index 2627d72a9c28c..cd6cd8ce50413 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant_context/types.tsx @@ -76,9 +76,6 @@ export interface AssistantAvailability { hasUpdateAIAssistantAnonymization: boolean; // When true, user has `Edit` privilege for `Global Knowledge Base` hasManageGlobalKnowledgeBase: boolean; - // When true, the Assistant Sharing feature is enabled - // keep type loose to avoid updating mocks as temp property - isAssistantSharingEnabled?: boolean; } export type GetAssistantMessages = (commentArgs: { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts index d1c82837a4704..5b1a508897631 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/index.ts @@ -205,7 +205,4 @@ export interface UseAssistantAvailability { hasUpdateAIAssistantAnonymization: boolean; // When true, user has `Edit` privilege for `Global Knowledge Base` hasManageGlobalKnowledgeBase: boolean; - // When true, the Assistant Sharing feature is enabled - // keep type loose to avoid updating mocks as temp property - isAssistantSharingEnabled?: boolean; } diff --git a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.ts b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.ts index e63de2962640f..4a35504aa9534 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/public/src/hooks/assistant_availability/use_assistant_availability.ts @@ -18,13 +18,8 @@ export const useAssistantAvailability = (): UseAssistantAvailability => { const isEnterprise = useLicense().isEnterprise(); const { application: { capabilities }, - featureFlags, } = useKibana().services; - const isAssistantSharingEnabled = featureFlags.getBooleanValue( - 'elasticAssistant.assistantSharingEnabled', - false - ); const hasAssistantPrivilege = capabilities[ASSISTANT_FEATURE_ID]?.['ai-assistant'] === true; const hasUpdateAIAssistantAnonymization = capabilities[ASSISTANT_FEATURE_ID]?.updateAIAssistantAnonymization === true; @@ -49,7 +44,6 @@ export const useAssistantAvailability = (): UseAssistantAvailability => { hasAssistantPrivilege, hasConnectorsAllPrivilege, hasConnectorsReadPrivilege, - isAssistantSharingEnabled, isAssistantEnabled: isEnterprise, isAssistantVisible: isEnterprise && isVisible, isAssistantManagementEnabled: isEnterprise && hasManageAssistantPrivilege, diff --git a/x-pack/solutions/security/test/security_solution_cypress/config.ts b/x-pack/solutions/security/test/security_solution_cypress/config.ts index a0e278fd6d7d6..318cf150ceb9d 100644 --- a/x-pack/solutions/security/test/security_solution_cypress/config.ts +++ b/x-pack/solutions/security/test/security_solution_cypress/config.ts @@ -53,7 +53,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { // in order to force Fleet to reach out to the registry to download the // packages listed in fleet_packages.json // See: https://elastic.slack.com/archives/CNMNXV4RG/p1683033379063079 - '--feature_flags.overrides.elasticAssistant.assistantSharingEnabled=true', `--xpack.fleet.developer.bundledPackageLocation=./inexistentDir`, `--xpack.securitySolution.enableExperimental=${JSON.stringify([ 'bulkEditAlertSuppressionEnabled', From 381d2ae6ec71cf4573ac4a45039f337d52ffae7f Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 4 Sep 2025 14:40:58 -0600 Subject: [PATCH 3/8] fix cypress --- .../ai_assistant/shared_conversations.cy.ts | 483 ++++++++---------- 1 file changed, 227 insertions(+), 256 deletions(-) diff --git a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts index 53dea3bbcb1a2..fb0b5e9e66328 100644 --- a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts +++ b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ROLES } from '@kbn/security-solution-plugin/common/test'; import type { MessageRole } from '@kbn/elastic-assistant-common'; import { closeToast } from '../../tasks/common/toast'; import { IS_SERVERLESS } from '../../env_var_names_constants'; @@ -23,7 +22,6 @@ import { copyUrlFromConversationSideContextMenu, copyUrlFromMenu, copyUrlFromShareModal, - createAndTitleConversation, dismissSharedCallout, duplicateConversation, duplicateFromConversationSideContextMenu, @@ -34,7 +32,6 @@ import { selectConversation, selectPrivate, selectShareModal, - shareConversation, shareConversations, shareConversationWithUser, submitShareModal, @@ -45,280 +42,254 @@ import { import { deleteConversations, waitForConversation } from '../../tasks/api_calls/assistant'; import { azureConnectorAPIPayload, createAzureConnector } from '../../tasks/api_calls/connectors'; import { deleteConnectors } from '../../tasks/api_calls/common'; -import { login } from '../../tasks/login'; +import { login, loginWithUser, logout } from '../../tasks/login'; import { visit, visitGetStartedPage } from '../../tasks/navigation'; const userRole: MessageRole = 'user'; const assistantRole: MessageRole = 'assistant'; -describe( - 'Assistant Conversation Sharing', - // skip on serverless until feature flag is lifted - { tags: ['@ess', '@serverless', '@skipInServerless', '@skipInServerlessMKI'] }, - () => { - const isServerless = Cypress.env(IS_SERVERLESS); - const primaryUser = isServerless ? 'elastic_admin' : 'system_indices_superuser'; - const secondaryUser = isServerless ? ROLES.soc_manager : 'elastic'; - const mockConvo1 = { - id: 'spooky', - title: 'Spooky convo', - messages: [ - { - timestamp: '2025-08-14T21:08:24.923Z', - content: 'Hi spooky robot', - user: { - name: primaryUser, - }, - role: userRole, +describe('Assistant Conversation Sharing', { tags: ['@ess', '@serverless'] }, () => { + const isServerless = Cypress.env(IS_SERVERLESS); + const primaryUser = isServerless ? 'elastic_admin' : 'system_indices_superuser'; + const secondaryUser = isServerless ? 'elastic_serverless' : 'elastic'; + const mockConvo1 = { + id: 'spooky', + title: 'Spooky convo', + messages: [ + { + timestamp: '2025-08-14T21:08:24.923Z', + content: 'Hi spooky robot', + user: { + name: primaryUser, }, - { - timestamp: '2025-08-14T21:08:25.349Z', - content: 'Hello spooky person', - role: assistantRole, + role: userRole, + }, + { + timestamp: '2025-08-14T21:08:25.349Z', + content: 'Hello spooky person', + role: assistantRole, + }, + ], + }; + const mockConvo2 = { + id: 'silly', + title: 'Silly convo', + messages: [ + { + timestamp: '2025-08-14T21:08:24.923Z', + content: 'Hi silly robot', + user: { + name: primaryUser, }, - ], - }; - const mockConvo2 = { - id: 'silly', - title: 'Silly convo', - messages: [ - { - timestamp: '2025-08-14T21:08:24.923Z', - content: 'Hi silly robot', - user: { - name: primaryUser, - }, - role: userRole, - }, - { - timestamp: '2025-08-14T21:08:25.349Z', - content: 'Hello silly person', - role: assistantRole, - }, - ], - }; - before(() => { - if (!isServerless) { - login(secondaryUser); - cy.clearCookies(); - } - }); - beforeEach(() => { - deleteConnectors(); - deleteConversations(); - login(isServerless ? 'admin' : undefined); - createAzureConnector(); - waitForConversation(mockConvo1); - waitForConversation(mockConvo2); - visitGetStartedPage(); - }); - it('Share modal works to not share, share globally, and share selected', () => { - openAssistant(); - selectConversation(mockConvo1.title); - selectConnector(azureConnectorAPIPayload.name); - // Assert that the conversation is not shared - assertCalloutState('private'); - // Open the share menu and verify not shared state - openShareMenu(); - assertShareMenuStatus('Private'); - // Selecting 'not shared' should not change sharing settings - selectPrivate(); - assertCalloutState('private'); - openShareMenu(); - assertShareMenuStatus('Private'); - // Opening and closing the share modal should not change sharing settings - selectShareModal(); - closeShareModal(); - assertCalloutState('private'); - openShareMenu(); - assertShareMenuStatus('Private'); - // Slecting global share changes sharing settings - selectGlobal(); - // submitShareModal(); success toast? - assertCalloutState('shared-by-me'); - closeToast(); - openShareMenu(); - assertShareMenuStatus('Shared'); - toggleConversationSideMenu(); - assertSharedConversationIcon(mockConvo1.title); - assertNotSharedConversationIcon(mockConvo2.title); - toggleConversationSideMenu(); - // Share the other conversation with selected users - selectConversation(mockConvo2.title); - selectConnector(azureConnectorAPIPayload.name); - // Assert that the conversation is not shared - assertCalloutState('private'); - openShareMenu(); - assertShareMenuStatus('Private'); - selectShareModal(); - // Press save without selecting users - submitShareModal(); - assertCalloutState('private'); - openShareMenu(); - assertShareMenuStatus('Private'); - selectShareModal(); - // Select secondaryUser to share the conversation - shareConversationWithUser(isServerless ? 'test_user' : secondaryUser); - submitShareModal(); - assertCalloutState('shared-by-me'); - openShareMenu(); - assertShareMenuStatus('Restricted'); - // Opens to selected share since conversation is shared with selected users - selectShareModal(); - assertShareUser(isServerless ? 'test_user' : secondaryUser); - closeShareModal(); + role: userRole, + }, + { + timestamp: '2025-08-14T21:08:25.349Z', + content: 'Hello silly person', + role: assistantRole, + }, + ], + }; + before(() => { + loginSecondaryUser(isServerless, secondaryUser); + logout(); + }); + beforeEach(() => { + deleteConnectors(); + deleteConversations(); + login(isServerless ? 'admin' : undefined); + createAzureConnector(); + waitForConversation(mockConvo1); + waitForConversation(mockConvo2); + visitGetStartedPage(); + }); + it('Share modal works to not share, share globally, and share selected', () => { + openAssistant(); + selectConversation(mockConvo1.title); + selectConnector(azureConnectorAPIPayload.name); + // Assert that the conversation is not shared + assertCalloutState('private'); + // Open the share menu and verify not shared state + openShareMenu(); + assertShareMenuStatus('Private'); + // Selecting 'not shared' should not change sharing settings + selectPrivate(); + assertCalloutState('private'); + openShareMenu(); + assertShareMenuStatus('Private'); + // Opening and closing the share modal should not change sharing settings + selectShareModal(); + closeShareModal(); + assertCalloutState('private'); + openShareMenu(); + assertShareMenuStatus('Private'); + // Slecting global share changes sharing settings + selectGlobal(); + // submitShareModal(); success toast? + assertCalloutState('shared-by-me'); + closeToast(); + openShareMenu(); + assertShareMenuStatus('Shared'); + toggleConversationSideMenu(); + assertSharedConversationIcon(mockConvo1.title); + assertNotSharedConversationIcon(mockConvo2.title); + toggleConversationSideMenu(); + // Share the other conversation with selected users + selectConversation(mockConvo2.title); + selectConnector(azureConnectorAPIPayload.name); + // Assert that the conversation is not shared + assertCalloutState('private'); + openShareMenu(); + assertShareMenuStatus('Private'); + selectShareModal(); + // Press save without selecting users + submitShareModal(); + assertCalloutState('private'); + openShareMenu(); + assertShareMenuStatus('Private'); + selectShareModal(); + // Select secondaryUser to share the conversation + shareConversationWithUser(secondaryUser); + submitShareModal(); + assertCalloutState('shared-by-me'); + openShareMenu(); + assertShareMenuStatus('Restricted'); + // Opens to selected share since conversation is shared with selected users + selectShareModal(); + assertShareUser(secondaryUser); + closeShareModal(); - toggleConversationSideMenu(); - assertSharedConversationIcon(mockConvo2.title); - }); - it('Shared conversations appear for the user they were shared with', () => { - shareConversations([ - { - title: mockConvo1.title, - share: 'global', - }, - { - title: mockConvo2.title, - // In serverless environments, we use 'test_user' instead of the regular secondary user due to configuration limitations. - // We can only create conversations via API with the primary user, so to minimize manual conversation creation in the UI, - // we're testing the 'selected user' sharing functionality only from the secondary user perspective. - share: isServerless ? 'test_user' : secondaryUser, - }, - ]); - // First logout admin user - cy.clearCookies(); + toggleConversationSideMenu(); + assertSharedConversationIcon(mockConvo2.title); + }); + it('Shared conversations appear for the user they were shared with', () => { + shareConversations([ + { + title: mockConvo1.title, + share: 'global', + }, + { + title: mockConvo2.title, + share: secondaryUser, + }, + ]); + // First logout admin user + cy.clearCookies(); - // Login as elastic user who should have access to shared conversations - login(secondaryUser); - visitGetStartedPage(); - openAssistant(); + // Login as elastic user who should have access to shared conversations + loginSecondaryUser(isServerless, secondaryUser); + visitGetStartedPage(); + openAssistant(); - // Check if the shared conversations are visible - toggleConversationSideMenu(); - cy.contains(mockConvo1.title).should('exist'); - if (isServerless) { - cy.contains(mockConvo2.title).should('not.exist'); - } else { - cy.contains(mockConvo2.title).should('exist'); - assertSharedConversationIcon(mockConvo2.title); - } - assertSharedConversationIcon(mockConvo1.title); - toggleConversationSideMenu(); + // Check if the shared conversations are visible + toggleConversationSideMenu(); + cy.contains(mockConvo1.title).should('exist'); - // Verify the first conversation is shared with secondaryUser - selectConversation(mockConvo1.title); - assertCalloutState('shared-with-me'); - // Ensure we can view messages in the shared conversation - assertMessageSent(mockConvo1.messages[0].content); + cy.contains(mockConvo2.title).should('exist'); + assertSharedConversationIcon(mockConvo2.title); - // only run this test if we are in serverless mode - // as the secondary user share works in ess - if (isServerless) { - const newConvoTitle = 'Other conversation'; - createAndTitleConversation(newConvoTitle); - shareConversation(primaryUser); - // First logout admin user - cy.clearCookies(); - login(isServerless ? 'admin' : undefined); - openAssistant(); - // Check if the selectedly shared conversation is visible - toggleConversationSideMenu(); - cy.contains(newConvoTitle).should('exist'); - assertSharedConversationIcon(newConvoTitle); - toggleConversationSideMenu(); - // Verify the first conversation is shared with secondaryUser - selectConversation(newConvoTitle); - assertCalloutState('shared-with-me'); - // Ensure we can view messages in the shared conversation - assertMessageSent('hello'); - } - }); - it('Dismissed callout remains dismissed when conversation is unselected and selected again', () => { - shareConversations([ - { - title: mockConvo1.title, - share: 'global', - }, - { - title: mockConvo2.title, - share: 'global', - }, - ]); - cy.clearCookies(); + assertSharedConversationIcon(mockConvo1.title); + toggleConversationSideMenu(); - login(secondaryUser); - visitGetStartedPage(); - openAssistant(); + // Verify the first conversation is shared with secondaryUser + selectConversation(mockConvo1.title); + assertCalloutState('shared-with-me'); + // Ensure we can view messages in the shared conversation + assertMessageSent(mockConvo1.messages[0].content); + }); + it('Dismissed callout remains dismissed when conversation is unselected and selected again', () => { + shareConversations([ + { + title: mockConvo1.title, + share: 'global', + }, + { + title: mockConvo2.title, + share: 'global', + }, + ]); + cy.clearCookies(); - selectConversation(mockConvo1.title); - assertCalloutState('shared-with-me'); - dismissSharedCallout(); + loginSecondaryUser(isServerless, secondaryUser); + visitGetStartedPage(); + openAssistant(); - selectConversation(mockConvo2.title); - assertCalloutState('shared-with-me'); + selectConversation(mockConvo1.title); + assertCalloutState('shared-with-me'); + dismissSharedCallout(); - selectConversation(mockConvo1.title); - assertNoSharedCallout(); - }); - it('Duplicate conversation allows user to continue a shared conversation', () => { - shareConversations([ - { - title: mockConvo1.title, - share: 'global', - }, - ]); + selectConversation(mockConvo2.title); + assertCalloutState('shared-with-me'); - cy.clearCookies(); + selectConversation(mockConvo1.title); + assertNoSharedCallout(); + }); + it('Duplicate conversation allows user to continue a shared conversation', () => { + shareConversations([ + { + title: mockConvo1.title, + share: 'global', + }, + ]); - login(secondaryUser); - visitGetStartedPage(); - openAssistant(); + cy.clearCookies(); - selectConversation(mockConvo1.title); - duplicateConversation(mockConvo1.title); - assertCalloutState('private'); - typeAndSendMessage('goodbye'); - assertMessageUser(primaryUser, 0); - assertMessageUser(`${isServerless ? 'test ' : ''}${secondaryUser}`, 2); - }); + loginSecondaryUser(isServerless, secondaryUser); + visitGetStartedPage(); + openAssistant(); - it('Duplicate conversation from conversation menu creates a duplicate', () => { - openAssistant(); - selectConversation(mockConvo1.title); - duplicateFromMenu(mockConvo1.title); - }); + selectConversation(mockConvo1.title); + duplicateConversation(mockConvo1.title); + assertCalloutState('private'); + typeAndSendMessage('goodbye'); + assertMessageUser(primaryUser, 0); + assertMessageUser(secondaryUser, 2); + }); - it('Duplicate conversation from conversation side menu creates a duplicate', () => { - openAssistant(); - toggleConversationSideMenu(); - duplicateFromConversationSideContextMenu(mockConvo2.title); - }); + it('Duplicate conversation from conversation menu creates a duplicate', () => { + openAssistant(); + selectConversation(mockConvo1.title); + duplicateFromMenu(mockConvo1.title); + }); - it('Copy URL copies the proper url from conversation menu', () => { - openAssistant(); - selectConversation(mockConvo1.title); - selectConnector(azureConnectorAPIPayload.name); - copyUrlFromMenu(); - // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion - }); - it('Copy URL copies the proper url from conversation side menu', () => { - openAssistant(); - toggleConversationSideMenu(); - copyUrlFromConversationSideContextMenu(); - // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion - }); + it('Duplicate conversation from conversation side menu creates a duplicate', () => { + openAssistant(); + toggleConversationSideMenu(); + duplicateFromConversationSideContextMenu(mockConvo2.title); + }); - it('Copy URL copies the proper url from share modal', () => { - openAssistant(); - selectConversation(mockConvo1.title); - selectConnector(azureConnectorAPIPayload.name); - copyUrlFromShareModal(); - // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion - }); + it('Copy URL copies the proper url from conversation menu', () => { + openAssistant(); + selectConversation(mockConvo1.title); + selectConnector(azureConnectorAPIPayload.name); + copyUrlFromMenu(); + // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion + }); + it('Copy URL copies the proper url from conversation side menu', () => { + openAssistant(); + toggleConversationSideMenu(); + copyUrlFromConversationSideContextMenu(); + // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion + }); - it('Visiting a URL with the assistant param opens the assistant to the proper conversation', () => { - cy.location('origin').then((origin) => { - visit(`${origin}/app/security/get_started?assistant=${mockConvo1.id}`); - }); - assertConversationTitle(mockConvo1.title); + it('Copy URL copies the proper url from share modal', () => { + openAssistant(); + selectConversation(mockConvo1.title); + selectConnector(azureConnectorAPIPayload.name); + copyUrlFromShareModal(); + // Cypress paste (doc.execCommand('paste')) is flaky, so skipping that assertion + }); + + it('Visiting a URL with the assistant param opens the assistant to the proper conversation', () => { + cy.location('origin').then((origin) => { + visit(`${origin}/app/security/get_started?assistant=${mockConvo1.id}`); }); + assertConversationTitle(mockConvo1.title); + }); +}); + +const loginSecondaryUser = (isServerless: boolean, username: string) => { + if (isServerless) { + loginWithUser({ username, password: 'changeme' }); + } else { + login(username); } -); +}; From fd7fe9d2973c5bb56aae0803eec2b8f4267a1422 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 4 Sep 2025 15:09:41 -0600 Subject: [PATCH 4/8] check --- .../cypress/e2e/ai_assistant/shared_conversations.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts index fb0b5e9e66328..43fe3b1caa0f5 100644 --- a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts +++ b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts @@ -110,7 +110,7 @@ describe('Assistant Conversation Sharing', { tags: ['@ess', '@serverless'] }, () // Open the share menu and verify not shared state openShareMenu(); assertShareMenuStatus('Private'); - // Selecting 'not shared' should not change sharing settings + // Selecting 'not shared' should not change sharing settings. selectPrivate(); assertCalloutState('private'); openShareMenu(); From 0ab19a1abfd86527171e84158dd90aac5729e899 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Tue, 9 Sep 2025 10:09:49 -0600 Subject: [PATCH 5/8] rm from convo settings --- .../conversation_settings_editor.test.tsx | 1 - .../conversation_settings_editor.tsx | 29 +-- .../index.tsx | 5 +- .../use_conversations_table.tsx | 227 +++++++++--------- 4 files changed, 121 insertions(+), 141 deletions(-) diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.test.tsx index 075d72844cf5e..27475b0c3e1c7 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.test.tsx @@ -91,7 +91,6 @@ const baseProps: ConversationSettingsEditorProps = { conversationSettings: { [welcomeConvo.id]: welcomeConvo }, conversationsSettingsBulkActions: {}, http: { basePath: { get: jest.fn(() => 'http://localhost:5601') } } as unknown as HttpSetup, - isAssistantSharingEnabled: true, isDisabled: false, selectedConversation: welcomeConvo, setConversationSettings: mockSetConversationSettings, diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx index 1be4db012a9f0..a492d440f114b 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx @@ -35,7 +35,6 @@ export interface ConversationSettingsEditorProps { conversationSettings: Record; conversationsSettingsBulkActions: ConversationsBulkActions; http: HttpSetup; - isAssistantSharingEnabled?: boolean; isDisabled?: boolean; selectedConversation: Conversation; setConversationSettings: React.Dispatch>>; @@ -52,7 +51,6 @@ export const ConversationSettingsEditor: React.FC )} - - {isAssistantSharingEnabled && ( - - - - )} + + + ); } diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx index 219fa8bbb1130..9cb437bd5f6ee 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx @@ -55,7 +55,7 @@ const ConversationSettingsManagementComponent: React.FC = ({ }) => { const { actionTypeRegistry, - assistantAvailability: { isAssistantEnabled, isAssistantSharingEnabled }, + assistantAvailability: { isAssistantEnabled }, http, nameSpace, toasts, @@ -250,7 +250,7 @@ const ConversationSettingsManagementComponent: React.FC = ({ onCancelClick(); }, [closeConfirmModal, handleUnselectAll, onCancelClick]); - const { getConversationsList, getColumns } = useConversationsTable(isAssistantSharingEnabled); + const { getConversationsList, getColumns } = useConversationsTable(); const conversationOptions = getConversationsList({ allSystemPrompts, @@ -373,7 +373,6 @@ const ConversationSettingsManagementComponent: React.FC = ({ conversationSettings={conversations} conversationsSettingsBulkActions={conversationsSettingsBulkActions} http={http} - isAssistantSharingEnabled={isAssistantSharingEnabled} isDisabled={isDisabled} selectedConversation={selectedConversation} setConversationSettings={setConversationSettings} diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.tsx index 518e5362c3faa..6030259c60d5e 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.tsx @@ -64,7 +64,7 @@ interface GetColumnsParams { totalItemCount: number; } -export const useConversationsTable = (isAssistantSharingEnabled: boolean = false) => { +export const useConversationsTable = () => { const getActions = useInlineActions(); const getColumns = useCallback( @@ -82,126 +82,113 @@ export const useConversationsTable = (isAssistantSharingEnabled: boolean = false onDeleteActionClicked, onEditActionClicked, totalItemCount, - }: GetColumnsParams): Array> => { - const columns: Array> = [ - { - field: '', - name: ( - - ), - render: (conversation: ConversationTableItem) => ( - - ), - width: '40px', - sortable: false, + }: GetColumnsParams): Array> => [ + { + field: '', + name: ( + + ), + render: (conversation: ConversationTableItem) => ( + + ), + width: '40px', + sortable: false, + }, + { + name: i18n.CONVERSATIONS_TABLE_COLUMN_TITLE, + render: (conversation: ConversationTableItem) => ( + onEditActionClicked(conversation)}>{conversation.title} + ), + sortable: ({ title }: ConversationTableItem) => title, + }, + { + field: 'systemPromptTitle', + name: i18n.CONVERSATIONS_TABLE_COLUMN_SYSTEM_PROMPT, + align: 'left', + render: (systemPromptTitle: ConversationTableItem['systemPromptTitle']) => + systemPromptTitle ? {systemPromptTitle} : null, + sortable: false, + }, + { + field: 'connectorTypeTitle', + name: i18n.CONVERSATIONS_TABLE_COLUMN_CONNECTOR, + align: 'left', + render: (connectorTypeTitle: ConversationTableItem['connectorTypeTitle']) => + connectorTypeTitle ? {connectorTypeTitle} : null, + sortable: false, + }, + { + name: i18n.CONVERSATIONS_TABLE_COLUMN_SHARING, + render: (conversation: ConversationTableItem) => { + const conversationSharedState = getConversationSharedState(conversation); + const sharingMap = { + [ConversationSharedState.SHARED]: { tooltip: VISIBLE_SHARED, badge: SHARED }, + [ConversationSharedState.RESTRICTED]: { + tooltip: VISIBLE_RESTRICTED, + badge: RESTRICTED, + }, + [ConversationSharedState.PRIVATE]: { tooltip: VISIBLE_PRIVATE, badge: PRIVATE }, + }; + + const { tooltip: tooltipContent, badge: badgeLabel } = + sharingMap[conversationSharedState] || sharingMap[ConversationSharedState.PRIVATE]; + return ( + + + + ); }, - { - name: i18n.CONVERSATIONS_TABLE_COLUMN_TITLE, - render: (conversation: ConversationTableItem) => ( - onEditActionClicked(conversation)}> - {conversation.title} - - ), - sortable: ({ title }: ConversationTableItem) => title, - }, - { - field: 'systemPromptTitle', - name: i18n.CONVERSATIONS_TABLE_COLUMN_SYSTEM_PROMPT, - align: 'left', - render: (systemPromptTitle: ConversationTableItem['systemPromptTitle']) => - systemPromptTitle ? {systemPromptTitle} : null, - sortable: false, - }, - { - field: 'connectorTypeTitle', - name: i18n.CONVERSATIONS_TABLE_COLUMN_CONNECTOR, - align: 'left', - render: (connectorTypeTitle: ConversationTableItem['connectorTypeTitle']) => - connectorTypeTitle ? {connectorTypeTitle} : null, - sortable: false, - }, - ]; - - // Only include sharing column if assistant sharing is enabled - if (isAssistantSharingEnabled) { - columns.push({ - name: i18n.CONVERSATIONS_TABLE_COLUMN_SHARING, - render: (conversation: ConversationTableItem) => { - const conversationSharedState = getConversationSharedState(conversation); - const sharingMap = { - [ConversationSharedState.SHARED]: { tooltip: VISIBLE_SHARED, badge: SHARED }, - [ConversationSharedState.RESTRICTED]: { - tooltip: VISIBLE_RESTRICTED, - badge: RESTRICTED, - }, - [ConversationSharedState.PRIVATE]: { tooltip: VISIBLE_PRIVATE, badge: PRIVATE }, - }; - - const { tooltip: tooltipContent, badge: badgeLabel } = - sharingMap[conversationSharedState] || sharingMap[ConversationSharedState.PRIVATE]; - return ( - - - - ); - }, - width: '100px', - }); - } - - columns.push( - { - field: 'updatedAt', - name: i18n.CONVERSATIONS_TABLE_COLUMN_UPDATED_AT, - align: 'center', - render: (updatedAt: ConversationTableItem['updatedAt']) => - updatedAt ? ( - - - - ) : null, - sortable: true, - }, - { - width: '120px', - align: 'center', - ...getActions({ - isDeleteEnabled, - isEditEnabled, - onDelete: onDeleteActionClicked, - onEdit: onEditActionClicked, - }), - } - ); - - return columns; - }, - [getActions, isAssistantSharingEnabled] + width: '100px', + }, + { + field: 'updatedAt', + name: i18n.CONVERSATIONS_TABLE_COLUMN_UPDATED_AT, + align: 'center', + render: (updatedAt: ConversationTableItem['updatedAt']) => + updatedAt ? ( + + + + ) : null, + sortable: true, + }, + { + width: '120px', + align: 'center', + ...getActions({ + isDeleteEnabled, + isEditEnabled, + onDelete: onDeleteActionClicked, + onEdit: onEditActionClicked, + }), + }, + ], + [getActions] ); const getConversationsList = useCallback( ({ From 1516e717a2af8011b7eddebef6ff4fd319e66d70 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Tue, 9 Sep 2025 10:13:41 -0600 Subject: [PATCH 6/8] test fixing --- .../conversation_list_item.test.tsx | 8 -------- .../kbn-elastic-assistant/impl/assistant/index.test.tsx | 8 +++++++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.test.tsx index 108d06ec447f1..beb8ca2537887 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/conversation_list_item.test.tsx @@ -174,12 +174,4 @@ describe('ConversationListItem', () => { expect(screen.getByTestId('convo-context-menu-item-duplicate')).toBeInTheDocument(); expect(screen.queryByTestId('convo-context-menu-item-delete')).not.toBeInTheDocument(); }); - - it('renders extraAction when sharing is disabled', () => { - render(); - // The extraAction is a delete button - expect(screen.getByTestId('delete-option')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('delete-option')); - expect(mockDelete).toHaveBeenCalledWith(expect.objectContaining({ id: ownerConvo.id })); - }); }); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.test.tsx index e6473664db6b2..7dc27edb1e41b 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.test.tsx @@ -43,6 +43,7 @@ const fullWelcomeConversation = { ...welcomeConvo, id: 'welcome_id', apiConfig, + isConversationOwner: true, }; const fullSheepConversation = { ...welcomeConvo, @@ -53,6 +54,7 @@ const fullSheepConversation = { replacements: {}, apiConfig, updatedAt: '2024-10-10T22:07:44.915Z', + isConversationOwner: true, }; const mockData = { @@ -169,7 +171,11 @@ describe('Assistant', () => { fireEvent.click(openConversationMenu); }); // selecting first conversation proves sort by updatedAt is working - const deleteButton = screen.getAllByTestId('delete-option')[0]; + const contextMenuButton = screen.getAllByTestId('convo-context-menu-button')[0]; + await act(async () => { + fireEvent.click(contextMenuButton); + }); + const deleteButton = screen.getByTestId('convo-context-menu-item-delete'); await act(async () => { fireEvent.click(deleteButton); }); From a4518bd28c8c99aff6cee3fb92981aacbf0994cd Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Tue, 9 Sep 2025 11:12:58 -0600 Subject: [PATCH 7/8] fix type --- .../use_conversations_table.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.test.tsx index cd0eb8d74b1e6..05e51f778ff5a 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/use_conversations_table.test.tsx @@ -31,7 +31,7 @@ const mockActionTypeRegistry: ActionTypeRegistryContract = { describe('useConversationsTable', () => { it('should return columns', () => { - const { result } = renderHook(() => useConversationsTable(true)); + const { result } = renderHook(() => useConversationsTable()); const columns = result.current.getColumns({ conversationOptions: [], deletedConversationsIds: [], From 78256792a334efecc92c6195b8af619418ccc370 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 11 Sep 2025 08:33:37 -0600 Subject: [PATCH 8/8] add secondary user test to duplicate test --- .../e2e/ai_assistant/shared_conversations.cy.ts | 14 +++++++++++++- .../cypress/tasks/login.ts | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts index 43fe3b1caa0f5..7c8eca4de2f82 100644 --- a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts +++ b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/ai_assistant/shared_conversations.cy.ts @@ -250,10 +250,22 @@ describe('Assistant Conversation Sharing', { tags: ['@ess', '@serverless'] }, () duplicateFromMenu(mockConvo1.title); }); - it('Duplicate conversation from conversation side menu creates a duplicate', () => { + it('Duplicate conversation from conversation side menu creates a duplicate and secondary user cannot access', () => { openAssistant(); toggleConversationSideMenu(); duplicateFromConversationSideContextMenu(mockConvo2.title); + + cy.clearCookies(); + + // Login as elastic user who should have access to shared conversations + loginSecondaryUser(isServerless, secondaryUser); + visitGetStartedPage(); + openAssistant(); + + // Check if the shared conversations are visible + toggleConversationSideMenu(); + cy.contains(mockConvo2.title).should('not.exist'); + cy.contains(`[Duplicate] ${mockConvo2.title}`).should('not.exist'); }); it('Copy URL copies the proper url from conversation menu', () => { diff --git a/x-pack/solutions/security/test/security_solution_cypress/cypress/tasks/login.ts b/x-pack/solutions/security/test/security_solution_cypress/cypress/tasks/login.ts index 28a2d07c1e10f..5c7793d162f70 100644 --- a/x-pack/solutions/security/test/security_solution_cypress/cypress/tasks/login.ts +++ b/x-pack/solutions/security/test/security_solution_cypress/cypress/tasks/login.ts @@ -103,4 +103,6 @@ export const loginWithUser = (user: User): void => { export const logout = (): void => { cy.visit(LOGOUT_URL); + // Wait for logout to complete before proceeding + cy.url().should('not.include', '/logout'); };