582 lines
18 KiB
TypeScript
582 lines
18 KiB
TypeScript
import { test, expect } from "../fixtures";
|
||
|
||
test.describe("知识库 - 创建知识库交互测试", () => {
|
||
test.beforeEach(async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/knowledge");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
});
|
||
|
||
test("点击创建知识库按钮打开对话框", async ({ authenticatedPage }) => {
|
||
const createBtn = authenticatedPage.getByRole("button", { name: /创建知识库/ }).first();
|
||
await expect(createBtn).toBeVisible({ timeout: 15000 });
|
||
await createBtn.click();
|
||
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 验证对话框标题
|
||
await expect(dialog.getByRole("heading", { name: "创建知识库" })).toBeVisible();
|
||
});
|
||
|
||
test("创建知识库对话框包含名称和描述输入框", async ({ authenticatedPage }) => {
|
||
const createBtn = authenticatedPage.getByRole("button", { name: /创建知识库/ }).first();
|
||
await expect(createBtn).toBeVisible({ timeout: 15000 });
|
||
await createBtn.click();
|
||
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 验证名称输入框
|
||
const nameInput = dialog.locator("#kb-name");
|
||
const hasNameInput = await nameInput.isVisible({ timeout: 5000 }).catch(() => false);
|
||
expect(hasNameInput).toBeTruthy();
|
||
|
||
// 验证描述输入框
|
||
const descTextarea = dialog.locator("#kb-desc");
|
||
const hasDesc = await descTextarea.isVisible({ timeout: 3000 }).catch(() => false);
|
||
expect(hasDesc).toBeTruthy();
|
||
});
|
||
|
||
test("填写知识库名称后创建按钮可点击", async ({ authenticatedPage }) => {
|
||
const createBtn = authenticatedPage.getByRole("button", { name: /创建知识库/ }).first();
|
||
await expect(createBtn).toBeVisible({ timeout: 15000 });
|
||
await createBtn.click();
|
||
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 初始状态下创建按钮应禁用
|
||
const submitBtn = dialog.getByRole("button", { name: "创建" });
|
||
const isDisabled = await submitBtn.isDisabled().catch(() => true);
|
||
expect(isDisabled).toBeTruthy();
|
||
|
||
// 填写名称
|
||
const nameInput = dialog.locator("#kb-name");
|
||
await nameInput.fill("E2E测试知识库");
|
||
|
||
// 创建按钮应变为可点击
|
||
await expect(submitBtn).toBeEnabled();
|
||
});
|
||
|
||
test("填写完整信息后点击创建提交", async ({ authenticatedPage }) => {
|
||
const createBtn = authenticatedPage.getByRole("button", { name: /创建知识库/ }).first();
|
||
await expect(createBtn).toBeVisible({ timeout: 15000 });
|
||
await createBtn.click();
|
||
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 填写名称
|
||
const nameInput = dialog.locator("#kb-name");
|
||
await nameInput.fill("E2E测试知识库");
|
||
|
||
// 填写描述
|
||
const descTextarea = dialog.locator("#kb-desc");
|
||
await descTextarea.fill("E2E自动化测试创建的知识库");
|
||
|
||
// 点击创建
|
||
const submitBtn = dialog.getByRole("button", { name: "创建" });
|
||
await submitBtn.click();
|
||
|
||
// 等待创建完成(对话框关闭或显示加载状态)
|
||
await authenticatedPage.waitForTimeout(3000);
|
||
});
|
||
|
||
test("点击取消按钮关闭创建对话框", async ({ authenticatedPage }) => {
|
||
const createBtn = authenticatedPage.getByRole("button", { name: /创建知识库/ }).first();
|
||
await expect(createBtn).toBeVisible({ timeout: 15000 });
|
||
await createBtn.click();
|
||
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 点击取消
|
||
const cancelBtn = dialog.getByRole("button", { name: "取消" });
|
||
await cancelBtn.click();
|
||
|
||
// 验证对话框关闭
|
||
await expect(dialog).not.toBeVisible({ timeout: 5000 });
|
||
});
|
||
});
|
||
|
||
test.describe("知识库 - 文档列表交互测试", () => {
|
||
test.beforeEach(async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/knowledge");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
});
|
||
|
||
test("切换企业知识库和行业知识库Tab", async ({ authenticatedPage }) => {
|
||
const enterpriseTab = authenticatedPage.getByRole("tab", { name: /企业知识库/ });
|
||
const industryTab = authenticatedPage.getByRole("tab", { name: /行业知识库/ });
|
||
|
||
const hasEnterprise = await enterpriseTab.isVisible({ timeout: 15000 }).catch(() => false);
|
||
const hasIndustry = await industryTab.isVisible({ timeout: 3000 }).catch(() => false);
|
||
|
||
if (!hasEnterprise && !hasIndustry) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 切换到行业知识库
|
||
if (hasIndustry) {
|
||
await industryTab.click();
|
||
await authenticatedPage.waitForTimeout(1000);
|
||
}
|
||
|
||
// 切换回企业知识库
|
||
if (hasEnterprise) {
|
||
await enterpriseTab.click();
|
||
await authenticatedPage.waitForTimeout(1000);
|
||
}
|
||
});
|
||
|
||
test("搜索知识库功能", async ({ authenticatedPage }) => {
|
||
// 查找搜索输入框
|
||
const searchInput = authenticatedPage.locator('input[placeholder*="搜索知识库"]');
|
||
const hasSearch = await searchInput.isVisible({ timeout: 15000 }).catch(() => false);
|
||
|
||
if (!hasSearch) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 输入搜索关键词
|
||
await searchInput.fill("测试搜索");
|
||
|
||
// 验证搜索已触发(前端过滤,无需等待API)
|
||
await authenticatedPage.waitForTimeout(500);
|
||
|
||
// 清空搜索
|
||
await searchInput.clear();
|
||
await authenticatedPage.waitForTimeout(500);
|
||
});
|
||
|
||
test("展开知识库卡片查看文档列表", async ({ authenticatedPage }) => {
|
||
// 等待知识库列表加载
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 查找知识库卡片
|
||
const kbCards = authenticatedPage.locator("div.grid.grid-cols-1 > div.space-y-3 > div.cursor-pointer, div.grid.gap-4 > div.space-y-3 > div.cursor-pointer");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
// 尝试更通用的选择器
|
||
const allCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const allCount = await allCards.count();
|
||
if (allCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
// 点击第一张卡片展开
|
||
await allCards.first().click();
|
||
} else {
|
||
// 点击第一张卡片展开
|
||
await kbCards.first().click();
|
||
}
|
||
|
||
// 等待文档列表加载
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
// 验证文档列表标题可见
|
||
const docListTitle = authenticatedPage.getByText("文档列表");
|
||
const hasDocList = await docListTitle.isVisible({ timeout: 10000 }).catch(() => false);
|
||
|
||
if (!hasDocList) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await expect(docListTitle).toBeVisible();
|
||
});
|
||
|
||
test("文档列表显示上传文档按钮", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 展开第一个知识库
|
||
const kbCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await kbCards.first().click();
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
// 验证上传文档按钮
|
||
const uploadBtn = authenticatedPage.getByRole("button", { name: /上传文档/ });
|
||
const hasUpload = await uploadBtn.isVisible({ timeout: 10000 }).catch(() => false);
|
||
|
||
if (!hasUpload) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await expect(uploadBtn).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test.describe("知识库 - 上传文档交互测试", () => {
|
||
test.beforeEach(async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/knowledge");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
});
|
||
|
||
test("点击上传文档按钮打开上传对话框", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 展开第一个知识库
|
||
const kbCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await kbCards.first().click();
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
// 点击上传文档按钮
|
||
const uploadBtn = authenticatedPage.getByRole("button", { name: /上传文档/ });
|
||
const hasUpload = await uploadBtn.isVisible({ timeout: 10000 }).catch(() => false);
|
||
|
||
if (!hasUpload) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await uploadBtn.click();
|
||
|
||
// 验证上传对话框
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 验证对话框标题
|
||
await expect(dialog.getByRole("heading", { name: "上传文档" })).toBeVisible();
|
||
});
|
||
|
||
test("上传文档对话框包含标题、来源类型和内容输入", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
const kbCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await kbCards.first().click();
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const uploadBtn = authenticatedPage.getByRole("button", { name: /上传文档/ });
|
||
const hasUpload = await uploadBtn.isVisible({ timeout: 10000 }).catch(() => false);
|
||
|
||
if (!hasUpload) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await uploadBtn.click();
|
||
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 验证标题输入框
|
||
const titleInput = dialog.locator("#doc-title");
|
||
const hasTitle = await titleInput.isVisible({ timeout: 5000 }).catch(() => false);
|
||
expect(hasTitle).toBeTruthy();
|
||
|
||
// 验证来源类型选择器
|
||
const sourceTypeLabel = dialog.getByText("来源类型");
|
||
const hasSourceType = await sourceTypeLabel.isVisible({ timeout: 3000 }).catch(() => false);
|
||
expect(hasSourceType).toBeTruthy();
|
||
});
|
||
|
||
test("切换来源类型为URL后显示URL输入框", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
const kbCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await kbCards.first().click();
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const uploadBtn = authenticatedPage.getByRole("button", { name: /上传文档/ });
|
||
const hasUpload = await uploadBtn.isVisible({ timeout: 10000 }).catch(() => false);
|
||
|
||
if (!hasUpload) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await uploadBtn.click();
|
||
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 点击来源类型下拉框
|
||
const sourceTypeTrigger = dialog.locator("button").filter({ hasText: /文本|URL|Markdown/ }).first();
|
||
const hasTrigger = await sourceTypeTrigger.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (!hasTrigger) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await sourceTypeTrigger.click();
|
||
|
||
// 选择URL选项
|
||
const urlOption = authenticatedPage.getByRole("option", { name: "URL" })
|
||
.or(authenticatedPage.locator("[data-radix-collection-item]").filter({ hasText: "URL" }));
|
||
const hasUrlOption = await urlOption.first().isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (!hasUrlOption) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await urlOption.first().click();
|
||
|
||
// 验证URL输入框出现
|
||
const urlInput = dialog.locator("#doc-url");
|
||
const hasUrlInput = await urlInput.isVisible({ timeout: 5000 }).catch(() => false);
|
||
expect(hasUrlInput).toBeTruthy();
|
||
});
|
||
|
||
test("填写文档信息后上传按钮可点击", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
const kbCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await kbCards.first().click();
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const uploadBtn = authenticatedPage.getByRole("button", { name: /上传文档/ });
|
||
const hasUpload = await uploadBtn.isVisible({ timeout: 10000 }).catch(() => false);
|
||
|
||
if (!hasUpload) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await uploadBtn.click();
|
||
|
||
const dialog = authenticatedPage.getByRole("dialog");
|
||
await expect(dialog).toBeVisible({ timeout: 10000 });
|
||
|
||
// 填写标题
|
||
const titleInput = dialog.locator("#doc-title");
|
||
await titleInput.fill("E2E测试文档");
|
||
|
||
// 填写内容(默认是文本类型)
|
||
const contentTextarea = dialog.locator("#doc-content");
|
||
const hasContent = await contentTextarea.isVisible({ timeout: 3000 }).catch(() => false);
|
||
|
||
if (hasContent) {
|
||
await contentTextarea.fill("这是E2E自动化测试上传的文档内容");
|
||
}
|
||
|
||
// 验证上传按钮可点击
|
||
const submitBtn = dialog.getByRole("button", { name: "上传" });
|
||
await expect(submitBtn).toBeEnabled();
|
||
});
|
||
});
|
||
|
||
test.describe("知识库 - 删除文档交互测试", () => {
|
||
test.beforeEach(async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/knowledge");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
});
|
||
|
||
test("展开知识库后文档列表中每行显示删除按钮", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 展开第一个知识库
|
||
const kbCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await kbCards.first().click();
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
// 查找文档表格
|
||
const docTable = authenticatedPage.locator("table");
|
||
const hasTable = await docTable.isVisible({ timeout: 10000 }).catch(() => false);
|
||
|
||
if (!hasTable) {
|
||
// 可能没有文档
|
||
const noDocs = authenticatedPage.getByText("暂无文档");
|
||
const hasNoDocs = await noDocs.isVisible({ timeout: 5000 }).catch(() => false);
|
||
if (hasNoDocs) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 验证操作列存在
|
||
const actionHeader = authenticatedPage.getByText("操作");
|
||
const hasActionHeader = await actionHeader.isVisible({ timeout: 5000 }).catch(() => false);
|
||
expect(hasActionHeader).toBeTruthy();
|
||
});
|
||
|
||
test("点击文档删除按钮移除文档", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 展开第一个知识库
|
||
const kbCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await kbCards.first().click();
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
// 查找文档表格行
|
||
const docRows = authenticatedPage.locator("table tbody tr");
|
||
const rowCount = await docRows.count();
|
||
|
||
if (rowCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 查找第一行的删除按钮
|
||
const firstRowDeleteBtn = docRows.first().locator("button").filter({
|
||
has: authenticatedPage.locator("svg.lucide-trash-2"),
|
||
});
|
||
const hasDeleteBtn = await firstRowDeleteBtn.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (!hasDeleteBtn) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 点击删除
|
||
await firstRowDeleteBtn.click();
|
||
|
||
// 等待删除完成
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
});
|
||
|
||
test("知识库卡片显示删除按钮", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.waitForTimeout(2000);
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有知识库");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (hasEmpty) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 查找知识库卡片中的删除按钮
|
||
const kbCards = authenticatedPage.locator("div.cursor-pointer.rounded-xl");
|
||
const cardCount = await kbCards.count();
|
||
|
||
if (cardCount === 0) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
// 在第一张卡片中查找删除按钮
|
||
const deleteBtn = kbCards.first().locator("button").filter({
|
||
has: authenticatedPage.locator("svg.lucide-trash-2"),
|
||
});
|
||
const hasDeleteBtn = await deleteBtn.isVisible({ timeout: 5000 }).catch(() => false);
|
||
|
||
if (!hasDeleteBtn) {
|
||
test.skip();
|
||
return;
|
||
}
|
||
|
||
await expect(deleteBtn).toBeVisible();
|
||
});
|
||
});
|