Some checks failed
Deploy / deploy (push) Has been cancelled
Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
432 lines
14 KiB
TypeScript
432 lines
14 KiB
TypeScript
/**
|
|
* 账号管理功能端到端测试
|
|
* 测试账号列表、创建、编辑、删除等功能
|
|
*/
|
|
|
|
import { test, expect, Page } from '@playwright/test';
|
|
|
|
const TEST_CONFIG = {
|
|
baseURL: 'http://localhost:5173',
|
|
timeout: 30000,
|
|
adminUser: {
|
|
username: 'admin',
|
|
password: '111111',
|
|
},
|
|
};
|
|
|
|
// 测试账号数据
|
|
const TEST_ACCOUNT = {
|
|
username: 'test_telegram_' + Date.now(),
|
|
nickname: '测试账号',
|
|
phone: '13800138000',
|
|
email: 'test@example.com',
|
|
status: 'active',
|
|
remark: '这是一个测试账号',
|
|
};
|
|
|
|
test.describe('账号管理功能测试', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
test.setTimeout(TEST_CONFIG.timeout);
|
|
|
|
// 登录系统
|
|
await page.goto(TEST_CONFIG.baseURL);
|
|
await loginAsAdmin(page);
|
|
|
|
// 导航到账号管理页面
|
|
await page.click('[data-testid="menu-account"]');
|
|
await page.click('[data-testid="menu-account-list"]');
|
|
await page.waitForURL(/\/account\/list/);
|
|
});
|
|
|
|
test('应该正确显示账号列表页面', async ({ page }) => {
|
|
// 检查页面标题
|
|
await expect(page.locator('.page-title')).toContainText(
|
|
/账号列表|Telegram账号管理/,
|
|
);
|
|
|
|
// 检查搜索表单
|
|
await expect(page.locator('[data-testid="search-username"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="search-status"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="search-button"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="reset-button"]')).toBeVisible();
|
|
|
|
// 检查操作按钮
|
|
await expect(
|
|
page.locator('[data-testid="add-account-button"]'),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.locator('[data-testid="batch-delete-button"]'),
|
|
).toBeVisible();
|
|
await expect(page.locator('[data-testid="export-button"]')).toBeVisible();
|
|
|
|
// 检查表格
|
|
await expect(page.locator('.ant-table')).toBeVisible();
|
|
await expect(page.locator('.ant-table-thead')).toBeVisible();
|
|
});
|
|
|
|
test('搜索功能应该正常工作', async ({ page }) => {
|
|
// 输入搜索条件
|
|
await page.fill('[data-testid="search-username"]', 'admin');
|
|
await page.selectOption('[data-testid="search-status"]', 'active');
|
|
|
|
// 点击搜索
|
|
await page.click('[data-testid="search-button"]');
|
|
|
|
// 等待搜索结果加载
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 检查是否显示了搜索结果
|
|
await expect(page.locator('.ant-table-tbody tr')).toHaveCount({ min: 1 });
|
|
|
|
// 检查搜索结果是否包含关键词
|
|
const firstRowUsername = await page
|
|
.locator('.ant-table-tbody tr:first-child td:nth-child(2)')
|
|
.textContent();
|
|
expect(firstRowUsername).toContain('admin');
|
|
|
|
// 测试重置功能
|
|
await page.click('[data-testid="reset-button"]');
|
|
await expect(page.locator('[data-testid="search-username"]')).toHaveValue(
|
|
'',
|
|
);
|
|
});
|
|
|
|
test('应该能创建新账号', async ({ page }) => {
|
|
// 点击添加账号按钮
|
|
await page.click('[data-testid="add-account-button"]');
|
|
|
|
// 等待弹窗出现
|
|
await expect(page.locator('.ant-modal')).toBeVisible();
|
|
await expect(page.locator('.ant-modal-title')).toContainText(
|
|
/添加账号|新增Telegram账号/,
|
|
);
|
|
|
|
// 填写表单数据
|
|
await page.fill('[data-testid="form-username"]', TEST_ACCOUNT.username);
|
|
await page.fill('[data-testid="form-nickname"]', TEST_ACCOUNT.nickname);
|
|
await page.fill('[data-testid="form-phone"]', TEST_ACCOUNT.phone);
|
|
await page.fill('[data-testid="form-email"]', TEST_ACCOUNT.email);
|
|
await page.selectOption('[data-testid="form-status"]', TEST_ACCOUNT.status);
|
|
await page.fill('[data-testid="form-remark"]', TEST_ACCOUNT.remark);
|
|
|
|
// 提交表单
|
|
await page.click('[data-testid="form-submit"]');
|
|
|
|
// 等待成功消息
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
await expect(page.locator('.ant-message-success')).toContainText(
|
|
/创建成功|添加成功/,
|
|
);
|
|
|
|
// 弹窗应该关闭
|
|
await expect(page.locator('.ant-modal')).not.toBeVisible();
|
|
|
|
// 在列表中应该能找到新创建的账号
|
|
await page.fill('[data-testid="search-username"]', TEST_ACCOUNT.username);
|
|
await page.click('[data-testid="search-button"]');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const foundAccount = page
|
|
.locator('.ant-table-tbody tr')
|
|
.filter({ hasText: TEST_ACCOUNT.username });
|
|
await expect(foundAccount).toBeVisible();
|
|
});
|
|
|
|
test('表单验证应该正常工作', async ({ page }) => {
|
|
await page.click('[data-testid="add-account-button"]');
|
|
await expect(page.locator('.ant-modal')).toBeVisible();
|
|
|
|
// 不填写必填字段直接提交
|
|
await page.click('[data-testid="form-submit"]');
|
|
|
|
// 应该显示验证错误
|
|
await expect(page.locator('.ant-form-item-explain-error')).toContainText(
|
|
/请输入用户名|用户名不能为空/,
|
|
);
|
|
|
|
// 输入无效的邮箱格式
|
|
await page.fill('[data-testid="form-username"]', 'test_user');
|
|
await page.fill('[data-testid="form-email"]', 'invalid-email');
|
|
await page.click('[data-testid="form-submit"]');
|
|
|
|
// 应该显示邮箱格式错误
|
|
await expect(page.locator('.ant-form-item-explain-error')).toContainText(
|
|
/邮箱格式不正确|请输入有效的邮箱地址/,
|
|
);
|
|
|
|
// 关闭弹窗
|
|
await page.click('.ant-modal-close');
|
|
});
|
|
|
|
test('应该能编辑账号信息', async ({ page }) => {
|
|
// 先搜索刚才创建的账号
|
|
await page.fill('[data-testid="search-username"]', TEST_ACCOUNT.username);
|
|
await page.click('[data-testid="search-button"]');
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 点击编辑按钮
|
|
const editButton = page
|
|
.locator('.ant-table-tbody tr')
|
|
.filter({ hasText: TEST_ACCOUNT.username })
|
|
.locator('[data-testid="edit-button"]');
|
|
await editButton.click();
|
|
|
|
// 等待编辑弹窗出现
|
|
await expect(page.locator('.ant-modal')).toBeVisible();
|
|
await expect(page.locator('.ant-modal-title')).toContainText(
|
|
/编辑账号|修改账号信息/,
|
|
);
|
|
|
|
// 修改昵称
|
|
const newNickname = '已修改的测试账号';
|
|
await page.fill('[data-testid="form-nickname"]', newNickname);
|
|
|
|
// 提交修改
|
|
await page.click('[data-testid="form-submit"]');
|
|
|
|
// 等待成功消息
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
await expect(page.locator('.ant-message-success')).toContainText(
|
|
/修改成功|更新成功/,
|
|
);
|
|
|
|
// 验证修改是否生效
|
|
await page.reload();
|
|
await page.fill('[data-testid="search-username"]', TEST_ACCOUNT.username);
|
|
await page.click('[data-testid="search-button"]');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const updatedRow = page
|
|
.locator('.ant-table-tbody tr')
|
|
.filter({ hasText: TEST_ACCOUNT.username });
|
|
await expect(updatedRow).toContainText(newNickname);
|
|
});
|
|
|
|
test('应该能查看账号详情', async ({ page }) => {
|
|
// 搜索账号
|
|
await page.fill('[data-testid="search-username"]', TEST_ACCOUNT.username);
|
|
await page.click('[data-testid="search-button"]');
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 点击查看按钮
|
|
const viewButton = page
|
|
.locator('.ant-table-tbody tr')
|
|
.filter({ hasText: TEST_ACCOUNT.username })
|
|
.locator('[data-testid="view-button"]');
|
|
await viewButton.click();
|
|
|
|
// 等待详情弹窗出现
|
|
await expect(page.locator('.ant-modal')).toBeVisible();
|
|
await expect(page.locator('.ant-modal-title')).toContainText(
|
|
/账号详情|查看账号信息/,
|
|
);
|
|
|
|
// 检查详情信息是否正确显示
|
|
await expect(page.locator('[data-testid="detail-username"]')).toContainText(
|
|
TEST_ACCOUNT.username,
|
|
);
|
|
await expect(page.locator('[data-testid="detail-phone"]')).toContainText(
|
|
TEST_ACCOUNT.phone,
|
|
);
|
|
await expect(page.locator('[data-testid="detail-email"]')).toContainText(
|
|
TEST_ACCOUNT.email,
|
|
);
|
|
|
|
// 关闭详情弹窗
|
|
await page.click('.ant-modal-close');
|
|
});
|
|
|
|
test('账号状态切换应该正常工作', async ({ page }) => {
|
|
// 搜索账号
|
|
await page.fill('[data-testid="search-username"]', TEST_ACCOUNT.username);
|
|
await page.click('[data-testid="search-button"]');
|
|
await page.waitForTimeout(1000);
|
|
|
|
const accountRow = page
|
|
.locator('.ant-table-tbody tr')
|
|
.filter({ hasText: TEST_ACCOUNT.username });
|
|
|
|
// 点击状态切换按钮
|
|
const statusSwitch = accountRow.locator('[data-testid="status-switch"]');
|
|
await statusSwitch.click();
|
|
|
|
// 确认状态变更
|
|
await page.click('[data-testid="confirm-status-change"]');
|
|
|
|
// 等待成功消息
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
await expect(page.locator('.ant-message-success')).toContainText(
|
|
/状态更新成功|操作成功/,
|
|
);
|
|
});
|
|
|
|
test('批量操作应该正常工作', async ({ page }) => {
|
|
// 选择多个账号
|
|
const checkboxes = page.locator('.ant-table-tbody .ant-checkbox-input');
|
|
await checkboxes.first().check();
|
|
await checkboxes.nth(1).check();
|
|
|
|
// 批量删除按钮应该变为可用状态
|
|
await expect(
|
|
page.locator('[data-testid="batch-delete-button"]'),
|
|
).not.toBeDisabled();
|
|
|
|
// 点击批量删除
|
|
await page.click('[data-testid="batch-delete-button"]');
|
|
|
|
// 确认删除操作
|
|
await expect(page.locator('.ant-modal-confirm')).toBeVisible();
|
|
await page.click('.ant-btn-primary');
|
|
|
|
// 等待操作完成
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
});
|
|
|
|
test('导出功能应该正常工作', async ({ page }) => {
|
|
// 设置下载事件监听
|
|
const downloadPromise = page.waitForEvent('download');
|
|
|
|
// 点击导出按钮
|
|
await page.click('[data-testid="export-button"]');
|
|
|
|
// 等待下载开始
|
|
const download = await downloadPromise;
|
|
|
|
// 验证下载文件名
|
|
expect(download.suggestedFilename()).toMatch(/账号列表.*\.xlsx$/);
|
|
});
|
|
|
|
test('分页功能应该正常工作', async ({ page }) => {
|
|
// 检查分页组件是否存在
|
|
await expect(page.locator('.ant-pagination')).toBeVisible();
|
|
|
|
// 如果有多页数据,测试分页切换
|
|
const nextButton = page.locator('.ant-pagination-next');
|
|
const isNextEnabled = await nextButton.isEnabled();
|
|
|
|
if (isNextEnabled) {
|
|
// 点击下一页
|
|
await nextButton.click();
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 检查页码是否改变
|
|
await expect(page.locator('.ant-pagination-item-active')).toContainText(
|
|
'2',
|
|
);
|
|
|
|
// 点击上一页
|
|
await page.click('.ant-pagination-prev');
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 检查是否回到第一页
|
|
await expect(page.locator('.ant-pagination-item-active')).toContainText(
|
|
'1',
|
|
);
|
|
}
|
|
});
|
|
|
|
test('表格排序功能应该正常工作', async ({ page }) => {
|
|
// 点击用户名列的排序按钮
|
|
const usernameHeader = page
|
|
.locator('.ant-table-thead th')
|
|
.filter({ hasText: /用户名|Username/ });
|
|
await usernameHeader.locator('.ant-table-column-sorter').click();
|
|
|
|
// 等待排序完成
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 检查是否有排序指示器
|
|
await expect(
|
|
usernameHeader.locator(
|
|
'.ant-table-column-sorter-up.active, .ant-table-column-sorter-down.active',
|
|
),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('应该能删除账号', async ({ page }) => {
|
|
// 搜索要删除的测试账号
|
|
await page.fill('[data-testid="search-username"]', TEST_ACCOUNT.username);
|
|
await page.click('[data-testid="search-button"]');
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 点击删除按钮
|
|
const deleteButton = page
|
|
.locator('.ant-table-tbody tr')
|
|
.filter({ hasText: TEST_ACCOUNT.username })
|
|
.locator('[data-testid="delete-button"]');
|
|
await deleteButton.click();
|
|
|
|
// 确认删除
|
|
await expect(page.locator('.ant-modal-confirm')).toBeVisible();
|
|
await page.click('.ant-btn-primary');
|
|
|
|
// 等待删除成功消息
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
await expect(page.locator('.ant-message-success')).toContainText(
|
|
/删除成功|操作成功/,
|
|
);
|
|
|
|
// 验证账号已被删除
|
|
await page.reload();
|
|
await page.fill('[data-testid="search-username"]', TEST_ACCOUNT.username);
|
|
await page.click('[data-testid="search-button"]');
|
|
await page.waitForTimeout(1000);
|
|
|
|
await expect(page.locator('.ant-empty')).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('账号导入功能测试', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto(TEST_CONFIG.baseURL);
|
|
await loginAsAdmin(page);
|
|
|
|
// 导航到账号导入页面
|
|
await page.click('[data-testid="menu-account"]');
|
|
await page.click('[data-testid="menu-account-import"]');
|
|
await page.waitForURL(/\/account\/import/);
|
|
});
|
|
|
|
test('应该正确显示导入页面', async ({ page }) => {
|
|
// 检查页面标题
|
|
await expect(page.locator('.page-title')).toContainText(
|
|
/账号导入|批量导入/,
|
|
);
|
|
|
|
// 检查上传组件
|
|
await expect(page.locator('.ant-upload-dragger')).toBeVisible();
|
|
|
|
// 检查模板下载链接
|
|
await expect(
|
|
page.locator('[data-testid="download-template"]'),
|
|
).toBeVisible();
|
|
|
|
// 检查导入记录表格
|
|
await expect(page.locator('.ant-table')).toBeVisible();
|
|
});
|
|
|
|
test('应该能下载导入模板', async ({ page }) => {
|
|
const downloadPromise = page.waitForEvent('download');
|
|
|
|
// 点击下载模板
|
|
await page.click('[data-testid="download-template"]');
|
|
|
|
const download = await downloadPromise;
|
|
expect(download.suggestedFilename()).toMatch(/账号导入模板.*\.xlsx$/);
|
|
});
|
|
});
|
|
|
|
// 辅助函数:管理员登录
|
|
async function loginAsAdmin(page: Page) {
|
|
await page.fill(
|
|
'[data-testid="username-input"]',
|
|
TEST_CONFIG.adminUser.username,
|
|
);
|
|
await page.fill(
|
|
'[data-testid="password-input"]',
|
|
TEST_CONFIG.adminUser.password,
|
|
);
|
|
await page.click('[data-testid="login-button"]');
|
|
await page.waitForURL(/\/dashboard/, { timeout: 10000 });
|
|
}
|