diff --git a/packages/frontend/core/src/blocksuite/ai/_common/config.ts b/packages/frontend/core/src/blocksuite/ai/_common/config.ts index 8a161f68bd..498e2cbebc 100644 --- a/packages/frontend/core/src/blocksuite/ai/_common/config.ts +++ b/packages/frontend/core/src/blocksuite/ai/_common/config.ts @@ -49,6 +49,7 @@ import { export const translateSubItem: AISubItemConfig[] = translateLangs.map(lang => { return { type: lang, + testId: `action-translate-${lang}`, handler: actionToHandler('translate', AIStarIconWithAnimation, { lang }), }; }); @@ -56,6 +57,7 @@ export const translateSubItem: AISubItemConfig[] = translateLangs.map(lang => { export const toneSubItem: AISubItemConfig[] = textTones.map(tone => { return { type: tone, + testId: `action-change-tone-${tone.toLowerCase()}`, handler: actionToHandler('changeTone', AIStarIconWithAnimation, { tone }), }; }); @@ -66,6 +68,7 @@ export function createImageFilterSubItem( return imageFilterStyles.map(style => { return { type: style, + testId: `action-image-filter-${style.toLowerCase().replace(' ', '-')}`, handler: actionToHandler( 'filterImage', AIImageIconWithAnimation, @@ -84,6 +87,7 @@ export function createImageProcessingSubItem( return imageProcessingTypes.map(type => { return { type, + testId: `action-image-processing-${type.toLowerCase().replace(' ', '-')}`, handler: actionToHandler( 'processImage', AIImageIconWithAnimation, @@ -146,36 +150,42 @@ const EditAIGroup: AIItemGroupConfig = { items: [ { name: 'Translate to', + testId: 'action-translate', icon: LanguageIcon(), showWhen: textBlockShowWhen, subItem: translateSubItem, }, { name: 'Change tone to', + testId: 'action-change-tone', icon: ToneIcon(), showWhen: textBlockShowWhen, subItem: toneSubItem, }, { name: 'Improve writing', + testId: 'action-improve-writing', icon: ImproveWritingIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('improveWriting', AIStarIconWithAnimation), }, { name: 'Make it longer', + testId: 'action-make-it-longer', icon: LongerIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('makeLonger', AIStarIconWithAnimation), }, { name: 'Make it shorter', + testId: 'action-make-it-shorter', icon: ShorterIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('makeShorter', AIStarIconWithAnimation), }, { name: 'Continue writing', + testId: 'action-continue-writing', icon: PenIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('continueWriting', AIPenIconWithAnimation), @@ -188,30 +198,35 @@ const DraftAIGroup: AIItemGroupConfig = { items: [ { name: 'Write an article about this', + testId: 'action-write-article', icon: PenIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('writeArticle', AIPenIconWithAnimation), }, { name: 'Write a tweet about this', + testId: 'action-write-twitter-post', icon: PenIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('writeTwitterPost', AIPenIconWithAnimation), }, { name: 'Write a poem about this', + testId: 'action-write-poem', icon: PenIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('writePoem', AIPenIconWithAnimation), }, { name: 'Write a blog post about this', + testId: 'action-write-blog-post', icon: PenIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('writeBlogPost', AIPenIconWithAnimation), }, { name: 'Brainstorm ideas about this', + testId: 'action-brainstorm', icon: PenIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('brainstorm', AIPenIconWithAnimation), @@ -224,36 +239,42 @@ const ReviewWIthAIGroup: AIItemGroupConfig = { items: [ { name: 'Fix spelling', + testId: 'action-fix-spelling', icon: DoneIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('fixSpelling', AIStarIconWithAnimation), }, { name: 'Fix grammar', + testId: 'action-fix-grammar', icon: DoneIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('improveGrammar', AIStarIconWithAnimation), }, { name: 'Explain this image', + testId: 'action-explain-image', icon: PenIcon(), showWhen: imageBlockShowWhen, handler: actionToHandler('explainImage', AIStarIconWithAnimation), }, { name: 'Explain this code', + testId: 'action-explain-code', icon: ExplainIcon(), showWhen: codeBlockShowWhen, handler: actionToHandler('explainCode', AIStarIconWithAnimation), }, { name: 'Check code error', + testId: 'action-check-code-error', icon: ExplainIcon(), showWhen: codeBlockShowWhen, handler: actionToHandler('checkCodeErrors', AIStarIconWithAnimation), }, { name: 'Explain selection', + testId: 'action-explain-selection', icon: SelectionIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('explain', AIStarIconWithAnimation), @@ -266,12 +287,14 @@ const GenerateWithAIGroup: AIItemGroupConfig = { items: [ { name: 'Summarize', + testId: 'action-summarize', icon: PenIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('summary', AIPenIconWithAnimation), }, { name: 'Generate headings', + testId: 'action-generate-headings', icon: PenIcon(), beta: true, handler: actionToHandler('createHeadings', AIPenIconWithAnimation), @@ -293,24 +316,28 @@ const GenerateWithAIGroup: AIItemGroupConfig = { }, { name: 'Generate an image', + testId: 'action-generate-image', icon: ImageIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('createImage', AIImageIconWithAnimation), }, { name: 'Generate outline', + testId: 'action-generate-outline', icon: PenIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('writeOutline', AIPenIconWithAnimation), }, { name: 'Brainstorm ideas with mind map', + testId: 'action-brainstorm-mindmap', icon: MindmapIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('brainstormMindmap', AIPenIconWithAnimation), }, { name: 'Generate presentation', + testId: 'action-generate-presentation', icon: PresentationIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('createSlides', AIPresentationIconWithAnimation), @@ -318,6 +345,7 @@ const GenerateWithAIGroup: AIItemGroupConfig = { }, { name: 'Make it real', + testId: 'action-make-it-real', icon: MakeItRealIcon(), beta: true, showWhen: textBlockShowWhen, @@ -325,6 +353,7 @@ const GenerateWithAIGroup: AIItemGroupConfig = { }, { name: 'Find actions', + testId: 'action-find-actions', icon: SearchIcon(), showWhen: textBlockShowWhen, handler: actionToHandler('findActions', AIStarIconWithAnimation), @@ -338,6 +367,7 @@ const OthersAIGroup: AIItemGroupConfig = { items: [ { name: 'Continue with AI', + testId: 'action-continue-with-ai', icon: CommentIcon(), handler: host => { const panel = getAIPanelWidget(host); @@ -366,6 +396,7 @@ export function buildAIImageItemGroups(): AIItemGroupConfig[] { items: [ { name: 'Explain this image', + testId: 'action-explain-image', icon: ImageIcon(), showWhen: () => true, handler: actionToHandler( @@ -382,6 +413,7 @@ export function buildAIImageItemGroups(): AIItemGroupConfig[] { items: [ { name: 'Generate an image', + testId: 'action-generate-image', icon: ImageIcon(), showWhen: () => true, handler: actionToHandler( @@ -393,6 +425,7 @@ export function buildAIImageItemGroups(): AIItemGroupConfig[] { }, { name: 'Image processing', + testId: 'action-image-processing', icon: ImageIcon(), showWhen: () => true, subItem: createImageProcessingSubItem(blockActionTrackerOptions), @@ -401,6 +434,7 @@ export function buildAIImageItemGroups(): AIItemGroupConfig[] { }, { name: 'AI image filter', + testId: 'action-ai-image-filter', icon: ImproveWritingIcon(), showWhen: () => true, subItem: createImageFilterSubItem(blockActionTrackerOptions), @@ -409,6 +443,7 @@ export function buildAIImageItemGroups(): AIItemGroupConfig[] { }, { name: 'Generate a caption', + testId: 'action-generate-caption', icon: PenIcon(), showWhen: () => true, beta: true, @@ -432,6 +467,7 @@ export function buildAICodeItemGroups(): AIItemGroupConfig[] { items: [ { name: 'Explain this code', + testId: 'action-explain-code', icon: ExplainIcon(), showWhen: () => true, handler: actionToHandler( @@ -443,6 +479,7 @@ export function buildAICodeItemGroups(): AIItemGroupConfig[] { }, { name: 'Check code error', + testId: 'action-check-code-error', icon: ExplainIcon(), showWhen: () => true, handler: actionToHandler( diff --git a/packages/frontend/core/src/blocksuite/ai/actions/edgeless-handler.ts b/packages/frontend/core/src/blocksuite/ai/actions/edgeless-handler.ts index 96854bd527..c8205782bf 100644 --- a/packages/frontend/core/src/blocksuite/ai/actions/edgeless-handler.ts +++ b/packages/frontend/core/src/blocksuite/ai/actions/edgeless-handler.ts @@ -462,7 +462,6 @@ export function noteBlockOrTextShowWhen( host: EditorHost ) { const selected = getCopilotSelectedElems(host); - return selected.some( el => el instanceof NoteBlockModel || diff --git a/packages/frontend/core/src/blocksuite/ai/actions/edgeless-response.ts b/packages/frontend/core/src/blocksuite/ai/actions/edgeless-response.ts index c594caa360..ab5b07cf74 100644 --- a/packages/frontend/core/src/blocksuite/ai/actions/edgeless-response.ts +++ b/packages/frontend/core/src/blocksuite/ai/actions/edgeless-response.ts @@ -85,6 +85,7 @@ export function discard( return { name: 'Discard', icon: DeleteIcon(), + testId: 'answer-discard', showWhen: () => !!panel.answer, handler: () => { panel.discard(); @@ -96,6 +97,7 @@ export function retry(panel: AffineAIPanelWidget): AIItemConfig { return { name: 'Retry', icon: ResetIcon(), + testId: 'answer-retry', handler: () => { reportResponse('result:retry'); panel.generate(); @@ -123,6 +125,7 @@ export function createInsertItems( icon: html`
${LightLoadingIcon}
`, + testId: 'answer-insert-below-loading', showWhen: () => { const panel = getAIPanelWidget(host); const data = ctx.get(); @@ -137,6 +140,8 @@ export function createInsertItems( { name: buttonText, icon: InsertBelowIcon(), + testId: + buttonText === 'Replace' ? 'answer-replace' : `answer-insert-below`, showWhen: () => { const panel = getAIPanelWidget(host); const data = ctx.get(); @@ -191,6 +196,7 @@ export function asCaption( return { name: 'Use as caption', icon: PenIcon(), + testId: 'answer-use-as-caption', showWhen: () => { const panel = getAIPanelWidget(host); return id === 'generateCaption' && !!panel.answer; @@ -553,9 +559,11 @@ export function actionToResponse( responses: [ { name: 'Response', + testId: 'answer-responses', items: [ { name: 'Continue in chat', + testId: 'answer-continue-in-chat', icon: ChatWithAiIcon({}), handler: () => { reportResponse('result:continue-in-chat'); diff --git a/packages/frontend/core/src/blocksuite/ai/ai-panel.ts b/packages/frontend/core/src/blocksuite/ai/ai-panel.ts index f40d9f29a7..bbe4233ba4 100644 --- a/packages/frontend/core/src/blocksuite/ai/ai-panel.ts +++ b/packages/frontend/core/src/blocksuite/ai/ai-panel.ts @@ -53,6 +53,7 @@ function asCaption( return { name: 'Use as caption', icon: PenIcon(), + testId: 'answer-use-as-caption', showWhen: () => { const panel = getAIPanelWidget(host); return id === 'generateCaption' && !!panel.answer; @@ -79,6 +80,7 @@ function createNewNote(host: EditorHost): AIItemConfig { return { name: 'Create new note', icon: PageIcon(), + testId: 'answer-create-new-note', showWhen: () => { const panel = getAIPanelWidget(host); return !!panel.answer && isInsideEdgelessEditor(host); @@ -147,9 +149,11 @@ function buildPageResponseConfig( return [ { name: 'Response', + testId: 'answer-responses', items: [ { name: 'Insert below', + testId: 'answer-insert-below', icon: InsertBelowIcon(), showWhen: () => !!panel.answer && (!id || !INSERT_ABOVE_ACTIONS.includes(id)), @@ -161,6 +165,7 @@ function buildPageResponseConfig( }, { name: 'Insert above', + testId: 'answer-insert-above', icon: InsertTopIcon(), showWhen: () => !!panel.answer && !!id && INSERT_ABOVE_ACTIONS.includes(id), @@ -173,6 +178,7 @@ function buildPageResponseConfig( asCaption(host, id), { name: 'Replace selection', + testId: 'answer-replace', icon: ReplaceIcon(), showWhen: () => !!panel.answer && !EXCLUDING_REPLACE_ACTIONS.includes(id), @@ -187,10 +193,12 @@ function buildPageResponseConfig( }, { name: '', + testId: 'answer-common-responses', items: [ { name: 'Continue in chat', icon: ChatWithAiIcon(), + testId: 'answer-continue-in-chat', handler: () => { reportResponse('result:continue-in-chat'); AIProvider.slots.requestOpenWithChat.next({ host }); @@ -200,6 +208,7 @@ function buildPageResponseConfig( { name: 'Regenerate', icon: ResetIcon(), + testId: 'answer-regenerate', handler: () => { reportResponse('result:retry'); panel.generate(); @@ -208,6 +217,7 @@ function buildPageResponseConfig( { name: 'Discard', icon: DeleteIcon(), + testId: 'answer-discard', handler: () => { panel.discard(); }, @@ -225,6 +235,7 @@ export function buildErrorResponseConfig(panel: AffineAIPanelWidget) { { name: 'Retry', icon: ResetIcon(), + testId: 'error-retry', showWhen: () => true, handler: () => { reportResponse('result:retry'); @@ -234,6 +245,7 @@ export function buildErrorResponseConfig(panel: AffineAIPanelWidget) { { name: 'Discard', icon: DeleteIcon(), + testId: 'error-discard', showWhen: () => !!panel.answer, handler: () => { panel.discard(); diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/action-wrapper.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/action-wrapper.ts index 911e5a574a..6fec66bf96 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/action-wrapper.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/action-wrapper.ts @@ -142,6 +142,7 @@ export class ActionWrapper extends WithDisposable(LitElement) {
(this.promptShow = !this.promptShow)} > ${icons[item.action] ? icons[item.action] : DoneIcon()} @@ -152,22 +153,27 @@ export class ActionWrapper extends WithDisposable(LitElement) {
${this.promptShow ? html` -
+
Answer
${HISTORY_IMAGE_ACTIONS.includes(item.action) ? images && html`` : nothing} ${answer - ? createTextRenderer(this.host, { customHeading: true })(answer) + ? createTextRenderer(this.host, { + customHeading: true, + testId: 'chat-message-action-answer', + })(answer) : nothing} ${originalText ? html`
Prompt
- ${createTextRenderer(this.host, { customHeading: true })( - item.messages[0].content + originalText - )}` + ${createTextRenderer(this.host, { + customHeading: true, + testId: 'chat-message-action-prompt', + })(item.messages[0].content + originalText)}` : nothing}
` diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/image-to-text.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/image-to-text.ts index 7584dac215..ac69d79485 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/image-to-text.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/image-to-text.ts @@ -1,6 +1,3 @@ -import './action-wrapper'; -import '../content/images'; - import { WithDisposable } from '@blocksuite/affine/global/lit'; import type { EditorHost } from '@blocksuite/affine/std'; import { ShadowlessElement } from '@blocksuite/affine/std'; @@ -27,7 +24,10 @@ export class ActionImageToText extends WithDisposable(ShadowlessElement) { })} > ${answer - ? html`` + ? html`` : nothing}
`; diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/image.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/image.ts index 8cf4d4e7e2..2cdbb92982 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/image.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/image.ts @@ -17,13 +17,19 @@ export class ActionImage extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) accessor host!: EditorHost; + @property({ attribute: 'data-testid', reflect: true }) + accessor testId = 'action-image'; + protected override render() { const images = this.item.messages[0].attachments; return html`
${images - ? html`` + ? html`` : nothing}
`; diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/text.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/text.ts index 253eb9999d..ea36b5cfb0 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/text.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/actions/text.ts @@ -54,6 +54,7 @@ export class ActionText extends WithDisposable(LitElement) { border: isCode ? 'none' : '1px solid var(--affine-border-color)', })} class="original-text" + data-testid="original-text" > ${createTextRenderer(this.host, { customHeading: true, diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/ai-loading.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/ai-loading.ts index 93a8d3ef29..0c4772adaa 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/ai-loading.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/ai-loading.ts @@ -47,6 +47,9 @@ export class AILoading extends WithDisposable(LitElement) { @property({ attribute: false }) accessor stopGenerating!: () => void; + @property({ attribute: 'data-testid', reflect: true }) + accessor testId = 'ai-loading'; + override render() { return html`
diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-chips.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-chips.ts index 6ffeb8a342..04502f26fd 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-chips.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-chips.ts @@ -99,6 +99,9 @@ export class ChatPanelChips extends SignalWatcher( @property({ attribute: false }) accessor searchMenuConfig!: SearchMenuConfig; + @property({ attribute: 'data-testid', reflect: true }) + accessor testId = 'chat-panel-chips'; + @query('.add-button') accessor addButton!: HTMLDivElement; @@ -137,7 +140,11 @@ export class ChatPanelChips extends SignalWatcher( const chips = isCollapsed ? allChips.slice(0, 1) : allChips; return html`
-
+
${PlusIcon()}
${repeat( diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-input.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-input.ts index 2884fcf02d..dbd2feb6a5 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-input.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-input.ts @@ -220,6 +220,9 @@ export class ChatPanelInput extends SignalWatcher(WithDisposable(LitElement)) { @property({ attribute: false }) accessor docDisplayConfig!: DocDisplayConfig; + @property({ attribute: 'data-testid', reflect: true }) + accessor testId = 'chat-panel-input-container'; + private get _isNetworkActive() { return ( !!this.networkSearchConfig.visible.value && @@ -335,7 +338,10 @@ export class ChatPanelInput extends SignalWatcher(WithDisposable(LitElement)) { ` : nothing} ${this.chatContextValue.quote - ? html`
+ ? html`
${repeat( getFirstTwoLines(this.chatContextValue.quote), line => line, @@ -420,6 +426,7 @@ export class ChatPanelInput extends SignalWatcher(WithDisposable(LitElement)) { : nothing} ${images.length < MaximumImageCount ? html`
${ChatAbortIcon}
` diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts index 2e9d57b4f6..e37a6f5e5b 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts @@ -31,7 +31,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { position: relative; } - .chat-panel-messages { + .chat-panel-messages-container { display: flex; flex-direction: column; gap: 24px; @@ -157,9 +157,16 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) accessor previewSpecBuilder!: SpecBuilder; - @query('.chat-panel-messages') + @query('.chat-panel-messages-container') accessor messagesContainer: HTMLDivElement | null = null; + @property({ + type: String, + attribute: 'data-testid', + reflect: true, + }) + accessor testId = 'chat-panel-messages'; + getScrollContainer(): HTMLDivElement | null { return this.messagesContainer; } @@ -168,12 +175,13 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { return this.isLoading || !this.host?.doc.get(FeatureFlagService).getFlag('enable_ai_onboarding') ? nothing - : html`
+ : html`
${repeat( AIPreloadConfig, config => config.text, config => { return html`
config.handler()} class="onboarding-item" > @@ -220,7 +228,8 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { return html`
this._debouncedOnScroll()} > ${filteredItems.length === 0 @@ -232,8 +241,12 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { )}
${this.isLoading - ? 'AFFiNE AI is loading history...' - : 'What can I help you with?'} + ? html`AFFiNE AI is loading history...` + : html`What can I help you with?`}
${this._renderAIOnboarding()}
` @@ -268,7 +281,11 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) { )}
${showDownIndicator && filteredItems.length > 0 - ? html`
+ ? html`
${ArrowDownIcon()}
` : nothing} diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/components/add-popover.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/components/add-popover.ts index 628991b7b9..7077c96ca0 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/components/add-popover.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/components/add-popover.ts @@ -44,6 +44,8 @@ export type MenuItem = { name: string | TemplateResult<1>; icon: TemplateResult<1>; action: MenuAction; + suffix?: string | TemplateResult<1>; + testId?: string; }; export type MenuAction = () => Promise | void; @@ -140,6 +142,7 @@ export class ChatPanelAddPopover extends SignalWatcher( { key: 'tags', name: 'Tags', + testId: 'ai-chat-with-tags', icon: TagsIcon(), action: () => { this._toggleMode(AddPopoverMode.Tags); @@ -148,6 +151,7 @@ export class ChatPanelAddPopover extends SignalWatcher( { key: 'collections', name: 'Collections', + testId: 'ai-chat-with-collections', icon: CollectionsIcon(), action: () => { this._toggleMode(AddPopoverMode.Collections); @@ -176,6 +180,7 @@ export class ChatPanelAddPopover extends SignalWatcher( { key: 'files', name: 'Upload files (pdf, txt, csv)', + testId: 'ai-chat-with-files', icon: UploadIcon(), action: this._addFileChip, }, @@ -330,13 +335,14 @@ export class ChatPanelAddPopover extends SignalWatcher( ${repeat( items, item => item.key, - ({ key, name, icon, action }, idx) => { + ({ key, name, icon, action, testId }, idx) => { const curIdx = startIndex + idx; return html` action()?.catch(console.error)} diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/content/pure-text.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/content/pure-text.ts index 5d43c3d227..b71fc0e866 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/content/pure-text.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/content/pure-text.ts @@ -37,6 +37,9 @@ export class ChatContentPureText extends ShadowlessElement { @property({ attribute: false }) accessor text: string = ''; + @property({ attribute: 'data-testid', reflect: true }) + accessor testId = 'chat-content-pure-text'; + protected override render() { return this.text.length > 0 ? html`
${this.text}
` diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/message/action.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/message/action.ts index 7e1499c487..f327fff0d8 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/message/action.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/message/action.ts @@ -16,6 +16,9 @@ export class ChatMessageAction extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) accessor item!: ChatAction; + @property({ attribute: 'data-testid', reflect: true }) + accessor testId = 'chat-message-action'; + renderHeader() { return html`