-
${isEmbedding ? `Embedding ${done}/${total}` : 'AFFiNE AI'}
+
+ ${isEmbedding
+ ? html`Embedding ${done}/${total}`
+ : 'AFFiNE AI'}
+
{
AIProvider.toggleGeneralAIOnboarding?.(true);
diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/add-popover.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/add-popover.ts
index 58f1519086..d2880fb6ae 100644
--- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/add-popover.ts
+++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-chips/add-popover.ts
@@ -254,6 +254,9 @@ export class ChatPanelAddPopover extends SignalWatcher(
@property({ attribute: false })
accessor abortController!: AbortController;
+ @property({ attribute: 'data-testid', reflect: true })
+ accessor testId: string = 'ai-search-input';
+
@query('.search-input')
accessor searchInput!: HTMLInputElement;
diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/collection/operations.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/collection/operations.tsx
index 9bad42e4a7..e337389eb7 100644
--- a/packages/frontend/core/src/modules/explorer/views/nodes/collection/operations.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/nodes/collection/operations.tsx
@@ -118,6 +118,7 @@ export const useExplorerCollectionNodeOperations = (
view: (
diff --git a/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx
index 03b7518ad9..465367008c 100644
--- a/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx
@@ -43,6 +43,7 @@ export const ExplorerTags = () => {
return (
{
test.beforeEach(async ({ page, utils }) => {
- await utils.testUtils.setupTestEnvironment(page);
+ await utils.testUtils.setupTestEnvironment(page, false);
await utils.chatPanel.openChatPanel(page);
});
diff --git a/tests/affine-cloud-copilot/e2e/basic/chat.spec.ts b/tests/affine-cloud-copilot/e2e/basic/chat.spec.ts
index 0d16e35684..7f0f12697f 100644
--- a/tests/affine-cloud-copilot/e2e/basic/chat.spec.ts
+++ b/tests/affine-cloud-copilot/e2e/basic/chat.spec.ts
@@ -256,7 +256,6 @@ test.describe('AIBasic/Chat', () => {
]);
const { actions } = await utils.chatPanel.getLatestAssistantMessage(page);
- await page.pause();
await actions.retry();
await utils.chatPanel.waitForHistory(page, [
diff --git a/tests/affine-cloud-copilot/e2e/chat-with/collections.spec.ts b/tests/affine-cloud-copilot/e2e/chat-with/collections.spec.ts
index d52d2bf452..23144d0eb2 100644
--- a/tests/affine-cloud-copilot/e2e/chat-with/collections.spec.ts
+++ b/tests/affine-cloud-copilot/e2e/chat-with/collections.spec.ts
@@ -1,3 +1,79 @@
+import { loginUser } from '@affine-test/kit/utils/cloud';
+import { expect } from '@playwright/test';
+
import { test } from '../base/base-test';
-test.describe('AIChatWith/Collections', () => {});
+test.describe('AIChatWith/Collections', () => {
+ test.beforeEach(async ({ page, utils }) => {
+ const user = await utils.testUtils.getUser();
+ await loginUser(page, user);
+ await utils.testUtils.setupTestEnvironment(page);
+ await utils.chatPanel.openChatPanel(page);
+
+ // Create two collections
+ await utils.editor.createCollectionAndDoc(
+ page,
+ 'Collection 1',
+ 'EEee is a cute cat'
+ );
+ await utils.editor.createCollectionAndDoc(
+ page,
+ 'Collection 2',
+ 'FFff is a cute dog'
+ );
+ });
+
+ test('should support chat with collection', async ({ page, utils }) => {
+ await utils.chatPanel.chatWithCollections(page, ['Collection 1']);
+ await utils.chatPanel.makeChat(page, 'What is EEee(Use English)');
+ await utils.chatPanel.waitForHistory(page, [
+ {
+ role: 'user',
+ content: 'What is EEee(Use English)',
+ },
+ {
+ role: 'assistant',
+ status: 'success',
+ },
+ ]);
+
+ await expect(async () => {
+ const { content, message } =
+ await utils.chatPanel.getLatestAssistantMessage(page);
+ expect(content).toMatch(/EEee.*cat/);
+ expect(await message.locator('affine-footnote-node').count()).toBe(1);
+ }).toPass();
+ });
+
+ test('should support chat with multiple collections', async ({
+ page,
+ utils,
+ }) => {
+ await utils.chatPanel.chatWithCollections(page, [
+ 'Collection 1',
+ 'Collection 2',
+ ]);
+ await utils.chatPanel.makeChat(
+ page,
+ 'What is EEee? What is FFff?(Use English)'
+ );
+ await utils.chatPanel.waitForHistory(page, [
+ {
+ role: 'user',
+ content: 'What is EEee? What is FFff?(Use English)',
+ },
+ {
+ role: 'assistant',
+ status: 'success',
+ },
+ ]);
+
+ await expect(async () => {
+ const { content, message } =
+ await utils.chatPanel.getLatestAssistantMessage(page);
+ expect(content).toMatch(/EEee.*cat/);
+ expect(content).toMatch(/FFff.*dog/);
+ expect(await message.locator('affine-footnote-node').count()).toBe(2);
+ }).toPass();
+ });
+});
diff --git a/tests/affine-cloud-copilot/e2e/chat-with/tags.spec.ts b/tests/affine-cloud-copilot/e2e/chat-with/tags.spec.ts
index 70e3ccd2ac..4d8990aea1 100644
--- a/tests/affine-cloud-copilot/e2e/chat-with/tags.spec.ts
+++ b/tests/affine-cloud-copilot/e2e/chat-with/tags.spec.ts
@@ -1,3 +1,61 @@
+import { loginUser } from '@affine-test/kit/utils/cloud';
+import { expect } from '@playwright/test';
+
import { test } from '../base/base-test';
-test.describe('AIChatWith/tags', () => {});
+test.describe('AIChatWith/tags', () => {
+ test.beforeEach(async ({ page, utils }) => {
+ const user = await utils.testUtils.getUser();
+ await loginUser(page, user);
+ await utils.testUtils.setupTestEnvironment(page);
+ await utils.chatPanel.openChatPanel(page);
+ await utils.editor.createTagAndDoc(page, 'Tag 1', 'EEee is a cute cat');
+ await utils.editor.createTagAndDoc(page, 'Tag 2', 'FFff is a cute dog');
+ });
+
+ test('should support chat with tag', async ({ page, utils }) => {
+ await utils.chatPanel.chatWithTags(page, ['Tag 1']);
+ await utils.chatPanel.makeChat(page, 'What is EEee(Use English)');
+ await utils.chatPanel.waitForHistory(page, [
+ {
+ role: 'user',
+ content: 'What is EEee(Use English)',
+ },
+ {
+ role: 'assistant',
+ status: 'success',
+ },
+ ]);
+ await expect(async () => {
+ const { content, message } =
+ await utils.chatPanel.getLatestAssistantMessage(page);
+ expect(content).toMatch(/EEee.*cat/);
+ expect(await message.locator('affine-footnote-node').count()).toBe(1);
+ }).toPass();
+ });
+
+ test('should support chat with multiple tags', async ({ page, utils }) => {
+ await utils.chatPanel.chatWithTags(page, ['Tag 1', 'Tag 2']);
+ await utils.chatPanel.makeChat(
+ page,
+ 'What is EEee? What is FFff?(Use English)'
+ );
+ await utils.chatPanel.waitForHistory(page, [
+ {
+ role: 'user',
+ content: 'What is EEee? What is FFff?(Use English)',
+ },
+ {
+ role: 'assistant',
+ status: 'success',
+ },
+ ]);
+ await expect(async () => {
+ const { content, message } =
+ await utils.chatPanel.getLatestAssistantMessage(page);
+ expect(content).toMatch(/EEee.*cat/);
+ expect(content).toMatch(/FFff.*dog/);
+ expect(await message.locator('affine-footnote-node').count()).toBe(2);
+ }).toPass();
+ });
+});
diff --git a/tests/affine-cloud-copilot/e2e/utils/chat-panel-utils.ts b/tests/affine-cloud-copilot/e2e/utils/chat-panel-utils.ts
index 4eec12cf5a..122f66dcab 100644
--- a/tests/affine-cloud-copilot/e2e/utils/chat-panel-utils.ts
+++ b/tests/affine-cloud-copilot/e2e/utils/chat-panel-utils.ts
@@ -225,7 +225,6 @@ export class ChatPanelUtils {
await expect(states.every(state => state === 'finished')).toBe(true);
}).toPass({ timeout: 20000 });
- await page.pause();
await this.makeChat(page, text);
}
@@ -249,6 +248,39 @@ export class ChatPanelUtils {
await this.makeChat(page, text);
}
+ public static async chatWithTags(page: Page, tags: string[]) {
+ for (const tag of tags) {
+ const withButton = await page.getByTestId('chat-panel-with-button');
+ await withButton.click();
+ const withMenu = await page.getByTestId('ai-add-popover');
+ await withMenu.getByTestId('ai-chat-with-tags').click();
+ await withMenu.getByText(tag).click();
+ await page.getByTestId('chat-panel-chips').getByText(tag);
+ }
+ await this.waitForEmbeddingProgress(page);
+ }
+
+ public static async chatWithCollections(page: Page, collections: string[]) {
+ for (const collection of collections) {
+ const withButton = await page.getByTestId('chat-panel-with-button');
+ await withButton.click();
+ const withMenu = await page.getByTestId('ai-add-popover');
+ await withMenu.getByTestId('ai-chat-with-collections').click();
+ await withMenu.getByText(collection).click();
+ await page.getByTestId('chat-panel-chips').getByText(collection);
+ }
+ await this.waitForEmbeddingProgress(page);
+ }
+
+ public static async waitForEmbeddingProgress(page: Page) {
+ await page.getByTestId('chat-panel-embedding-progress').waitFor({
+ state: 'visible',
+ });
+ await page.getByTestId('chat-panel-embedding-progress').waitFor({
+ state: 'hidden',
+ });
+ }
+
public static async enableNetworkSearch(page: Page) {
const networkSearch = await page.getByTestId('chat-network-search');
if ((await networkSearch.getAttribute('data-active')) === 'false') {
diff --git a/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts b/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts
index 77394ddd65..14c47721ab 100644
--- a/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts
+++ b/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts
@@ -209,6 +209,63 @@ export class EditorUtils {
);
}
+ public static async createCollectionAndDoc(
+ page: Page,
+ collectionName: string,
+ docContent: string
+ ) {
+ // Create collection
+ await page.getByTestId('explorer-bar-add-collection-button').click();
+ const input = await page.getByTestId('prompt-modal-input');
+ await input.focus();
+ await input.pressSequentially(collectionName);
+ await page.getByTestId('prompt-modal-confirm').click();
+ const collections = await page.getByTestId('collapsible-section-content');
+ const collection = await collections
+ .getByText(collectionName)
+ .locator('..');
+
+ // Create doc
+ await collection.hover();
+ await collection.getByTestId('collection-add-doc-button').click();
+ await page.getByTestId('confirm-modal-confirm').click();
+ await this.focusToEditor(page);
+ const texts = docContent.split('\n');
+ for (const [index, line] of texts.entries()) {
+ await page.keyboard.insertText(line);
+ if (index !== texts.length - 1) {
+ await page.keyboard.press('Enter');
+ }
+ }
+ }
+
+ public static async createTagAndDoc(
+ page: Page,
+ tagName: string,
+ docContent: string
+ ) {
+ // Create tag
+ const tags = await page.getByTestId('explorer-tags');
+ await tags.getByTestId('explorer-bar-add-favorite-button').click();
+ const input = await page.getByTestId('rename-modal-input');
+ await input.focus();
+ await input.pressSequentially(tagName);
+ await input.press('Enter');
+ const tag = await tags.getByText(tagName).locator('..');
+
+ // Create doc
+ await tag.hover();
+ await tag.getByTestId('tag-add-doc-button').click();
+ await this.focusToEditor(page);
+ const texts = docContent.split('\n');
+ for (const [index, line] of texts.entries()) {
+ await page.keyboard.insertText(line);
+ if (index !== texts.length - 1) {
+ await page.keyboard.press('Enter');
+ }
+ }
+ }
+
public static async selectElementInEdgeless(page: Page, elements: string[]) {
await page.evaluate(
({ elements }) => {
diff --git a/tests/affine-cloud-copilot/e2e/utils/test-utils.ts b/tests/affine-cloud-copilot/e2e/utils/test-utils.ts
index 46a30cdfda..2f51a0a9de 100644
--- a/tests/affine-cloud-copilot/e2e/utils/test-utils.ts
+++ b/tests/affine-cloud-copilot/e2e/utils/test-utils.ts
@@ -1,4 +1,7 @@
-import { createRandomAIUser } from '@affine-test/kit/utils/cloud';
+import {
+ createRandomAIUser,
+ enableCloudWorkspace,
+} from '@affine-test/kit/utils/cloud';
import { openHomePage, setCoreUrl } from '@affine-test/kit/utils/load-page';
import {
clickNewPageButton,
@@ -52,10 +55,13 @@ export class TestUtils {
};
}
- public async setupTestEnvironment(page: Page) {
+ public async setupTestEnvironment(page: Page, enableCloud: boolean = true) {
await openHomePage(page);
await clickNewPageButton(page);
await waitForEditorLoad(page);
+ if (enableCloud) {
+ await enableCloudWorkspace(page);
+ }
}
public async createTestWorkspace(page: Page, name: string = 'test') {