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>
568 lines
19 KiB
TypeScript
568 lines
19 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_CAMPAIGN = {
|
|
name: '测试营销活动_' + Date.now(),
|
|
type: 'promotion',
|
|
budget: '10000',
|
|
startDate: '2024-02-01',
|
|
endDate: '2024-02-28',
|
|
description: '这是一个测试营销活动,用于验证系统功能',
|
|
targetAudience: 'all',
|
|
channels: ['telegram', 'email'],
|
|
};
|
|
|
|
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-marketing"]');
|
|
await page.click('[data-testid="menu-smart-campaign"]');
|
|
await page.waitForURL(/\/marketing\/smart-campaign/);
|
|
});
|
|
|
|
test('应该正确显示智能活动页面', async ({ page }) => {
|
|
// 检查页面标题
|
|
await expect(page.locator('.page-title')).toContainText(
|
|
/智能活动|营销活动/,
|
|
);
|
|
|
|
// 检查活动概览卡片
|
|
await expect(page.locator('.campaign-overview')).toBeVisible();
|
|
await expect(page.locator('[data-testid="total-campaigns"]')).toBeVisible();
|
|
await expect(
|
|
page.locator('[data-testid="active-campaigns"]'),
|
|
).toBeVisible();
|
|
await expect(page.locator('[data-testid="total-reach"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="conversion-rate"]')).toBeVisible();
|
|
|
|
// 检查创建活动按钮
|
|
await expect(
|
|
page.locator('[data-testid="create-campaign-button"]'),
|
|
).toBeVisible();
|
|
|
|
// 检查活动列表
|
|
await expect(page.locator('.campaign-list, .ant-table')).toBeVisible();
|
|
});
|
|
|
|
test('应该能创建新的营销活动', async ({ page }) => {
|
|
// 点击创建活动按钮
|
|
await page.click('[data-testid="create-campaign-button"]');
|
|
|
|
// 等待创建页面加载
|
|
await page.waitForURL(/\/marketing\/smart-campaign\/create/);
|
|
|
|
// 第一步:基本信息
|
|
await page.fill('[data-testid="campaign-name"]', TEST_CAMPAIGN.name);
|
|
await page.selectOption(
|
|
'[data-testid="campaign-type"]',
|
|
TEST_CAMPAIGN.type,
|
|
);
|
|
await page.fill('[data-testid="campaign-budget"]', TEST_CAMPAIGN.budget);
|
|
await page.fill(
|
|
'[data-testid="campaign-description"]',
|
|
TEST_CAMPAIGN.description,
|
|
);
|
|
await page.click('[data-testid="next-step"]');
|
|
|
|
// 第二步:目标受众
|
|
await page.click(
|
|
`[data-testid="audience-${TEST_CAMPAIGN.targetAudience}"]`,
|
|
);
|
|
await page.click('[data-testid="next-step"]');
|
|
|
|
// 第三步:推广渠道
|
|
for (const channel of TEST_CAMPAIGN.channels) {
|
|
await page.check(`[data-testid="channel-${channel}"]`);
|
|
}
|
|
await page.click('[data-testid="next-step"]');
|
|
|
|
// 第四步:时间设置
|
|
await page.fill('[data-testid="start-date"]', TEST_CAMPAIGN.startDate);
|
|
await page.fill('[data-testid="end-date"]', TEST_CAMPAIGN.endDate);
|
|
await page.click('[data-testid="next-step"]');
|
|
|
|
// 第五步:确认创建
|
|
await expect(page.locator('.campaign-summary')).toBeVisible();
|
|
await expect(page.locator('.campaign-summary')).toContainText(
|
|
TEST_CAMPAIGN.name,
|
|
);
|
|
await page.click('[data-testid="create-campaign"]');
|
|
|
|
// 等待成功消息
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
await expect(page.locator('.ant-message-success')).toContainText(
|
|
/创建成功|添加成功/,
|
|
);
|
|
|
|
// 应该跳转回活动列表
|
|
await page.waitForURL(/\/marketing\/smart-campaign$/);
|
|
});
|
|
|
|
test('活动状态管理应该正常工作', async ({ page }) => {
|
|
// 找到刚创建的活动
|
|
const campaignRow = page
|
|
.locator('.campaign-item, .ant-table-tbody tr')
|
|
.filter({ hasText: TEST_CAMPAIGN.name });
|
|
|
|
if ((await campaignRow.count()) > 0) {
|
|
// 点击启动活动按钮
|
|
await campaignRow.locator('[data-testid="start-campaign"]').click();
|
|
|
|
// 确认启动
|
|
await page.click('[data-testid="confirm-start"]');
|
|
|
|
// 等待状态更新
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
|
|
// 检查活动状态是否变为进行中
|
|
await page.reload();
|
|
await expect(campaignRow.locator('.campaign-status')).toContainText(
|
|
/进行中|active/,
|
|
);
|
|
|
|
// 测试暂停功能
|
|
await campaignRow.locator('[data-testid="pause-campaign"]').click();
|
|
await page.click('[data-testid="confirm-pause"]');
|
|
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('活动详情页面应该正常显示', async ({ page }) => {
|
|
// 点击第一个活动的详情按钮
|
|
const detailButton = page
|
|
.locator('.campaign-item, .ant-table-tbody tr')
|
|
.first()
|
|
.locator('[data-testid="view-detail"]');
|
|
await detailButton.click();
|
|
|
|
// 等待详情页面加载
|
|
await page.waitForURL(/\/marketing\/smart-campaign\/\d+/);
|
|
|
|
// 检查活动基本信息
|
|
await expect(page.locator('.campaign-info')).toBeVisible();
|
|
await expect(page.locator('.campaign-progress')).toBeVisible();
|
|
|
|
// 检查效果统计图表
|
|
await expect(page.locator('.performance-charts')).toBeVisible();
|
|
await expect(page.locator('.echarts-container')).toBeVisible();
|
|
|
|
// 检查目标受众分析
|
|
await expect(page.locator('.audience-analysis')).toBeVisible();
|
|
|
|
// 检查渠道效果对比
|
|
await expect(page.locator('.channel-comparison')).toBeVisible();
|
|
});
|
|
|
|
test('智能推荐功能应该正常工作', async ({ page }) => {
|
|
// 点击智能推荐按钮
|
|
await page.click('[data-testid="ai-recommendations"]');
|
|
|
|
// 等待推荐结果加载
|
|
await expect(page.locator('.ai-recommendations-panel')).toBeVisible();
|
|
|
|
// 检查推荐内容
|
|
await expect(page.locator('.recommendation-item')).toHaveCount({ min: 1 });
|
|
|
|
// 点击采纳推荐
|
|
const firstRecommendation = page.locator('.recommendation-item').first();
|
|
await firstRecommendation
|
|
.locator('[data-testid="adopt-recommendation"]')
|
|
.click();
|
|
|
|
// 确认采纳
|
|
await page.click('[data-testid="confirm-adopt"]');
|
|
|
|
// 等待成功消息
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
});
|
|
|
|
test('活动复制功能应该正常工作', async ({ page }) => {
|
|
// 找到一个活动并点击复制
|
|
const campaignRow = page
|
|
.locator('.campaign-item, .ant-table-tbody tr')
|
|
.first();
|
|
await campaignRow.locator('[data-testid="copy-campaign"]').click();
|
|
|
|
// 等待复制弹窗
|
|
await expect(page.locator('.ant-modal')).toBeVisible();
|
|
await expect(page.locator('.ant-modal-title')).toContainText(
|
|
/复制活动|克隆活动/,
|
|
);
|
|
|
|
// 修改活动名称
|
|
const newName = '复制的营销活动_' + Date.now();
|
|
await page.fill('[data-testid="copy-campaign-name"]', newName);
|
|
|
|
// 确认复制
|
|
await page.click('[data-testid="confirm-copy"]');
|
|
|
|
// 等待成功消息
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
|
|
// 验证复制的活动出现在列表中
|
|
await page.reload();
|
|
await expect(
|
|
page
|
|
.locator('.campaign-item, .ant-table-tbody tr')
|
|
.filter({ hasText: newName }),
|
|
).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('营销中心 - 客户分析', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto(TEST_CONFIG.baseURL);
|
|
await loginAsAdmin(page);
|
|
|
|
// 导航到客户分析页面
|
|
await page.click('[data-testid="menu-marketing"]');
|
|
await page.click('[data-testid="menu-customer-analysis"]');
|
|
await page.waitForURL(/\/marketing\/customer-analysis/);
|
|
});
|
|
|
|
test('应该正确显示客户分析页面', async ({ page }) => {
|
|
// 检查页面标题
|
|
await expect(page.locator('.page-title')).toContainText(
|
|
/客户分析|用户分析/,
|
|
);
|
|
|
|
// 检查分析概览
|
|
await expect(page.locator('.analysis-overview')).toBeVisible();
|
|
await expect(page.locator('[data-testid="total-customers"]')).toBeVisible();
|
|
await expect(
|
|
page.locator('[data-testid="active-customers"]'),
|
|
).toBeVisible();
|
|
await expect(page.locator('[data-testid="customer-growth"]')).toBeVisible();
|
|
|
|
// 检查分析图表
|
|
await expect(page.locator('.analysis-charts')).toBeVisible();
|
|
await expect(page.locator('.echarts-container')).toHaveCount({ min: 2 });
|
|
|
|
// 检查客户分群
|
|
await expect(page.locator('.customer-segments')).toBeVisible();
|
|
});
|
|
|
|
test('客户分群功能应该正常工作', async ({ page }) => {
|
|
// 点击创建分群按钮
|
|
await page.click('[data-testid="create-segment"]');
|
|
|
|
// 等待分群创建弹窗
|
|
await expect(page.locator('.ant-modal')).toBeVisible();
|
|
await expect(page.locator('.ant-modal-title')).toContainText(
|
|
/创建分群|新建分群/,
|
|
);
|
|
|
|
// 填写分群信息
|
|
const segmentName = '测试客户分群_' + Date.now();
|
|
await page.fill('[data-testid="segment-name"]', segmentName);
|
|
|
|
// 设置分群条件
|
|
await page.selectOption('[data-testid="condition-field"]', 'last_login');
|
|
await page.selectOption('[data-testid="condition-operator"]', 'within');
|
|
await page.fill('[data-testid="condition-value"]', '30');
|
|
await page.selectOption('[data-testid="condition-unit"]', 'days');
|
|
|
|
// 添加更多条件
|
|
await page.click('[data-testid="add-condition"]');
|
|
await page.selectOption(
|
|
'[data-testid="condition-field-2"]',
|
|
'registration_date',
|
|
);
|
|
await page.selectOption('[data-testid="condition-operator-2"]', 'after');
|
|
await page.fill('[data-testid="condition-value-2"]', '2024-01-01');
|
|
|
|
// 预览分群结果
|
|
await page.click('[data-testid="preview-segment"]');
|
|
await expect(page.locator('.segment-preview')).toBeVisible();
|
|
|
|
// 保存分群
|
|
await page.click('[data-testid="save-segment"]');
|
|
|
|
// 等待成功消息
|
|
await expect(page.locator('.ant-message-success')).toBeVisible();
|
|
});
|
|
|
|
test('客户行为分析应该正常工作', async ({ page }) => {
|
|
// 切换到行为分析标签
|
|
await page.click('[data-testid="tab-behavior-analysis"]');
|
|
|
|
// 检查行为分析图表
|
|
await expect(page.locator('.behavior-analysis')).toBeVisible();
|
|
await expect(page.locator('.user-journey')).toBeVisible();
|
|
await expect(page.locator('.conversion-funnel')).toBeVisible();
|
|
|
|
// 选择分析时间范围
|
|
await page.click('[data-testid="time-range-selector"]');
|
|
await page.click('[data-testid="range-30days"]');
|
|
|
|
// 等待图表更新
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 检查图表是否重新加载
|
|
await expect(page.locator('.echarts-container')).toBeVisible();
|
|
});
|
|
|
|
test('客户价值分析应该正常工作', async ({ page }) => {
|
|
// 切换到价值分析标签
|
|
await page.click('[data-testid="tab-value-analysis"]');
|
|
|
|
// 检查价值分析内容
|
|
await expect(page.locator('.value-analysis')).toBeVisible();
|
|
await expect(page.locator('.ltv-chart')).toBeVisible(); // 客户生命周期价值
|
|
await expect(page.locator('.rfm-analysis')).toBeVisible(); // RFM分析
|
|
|
|
// 检查高价值客户列表
|
|
await expect(page.locator('.high-value-customers')).toBeVisible();
|
|
await expect(page.locator('.ant-table')).toBeVisible();
|
|
|
|
// 点击导出高价值客户
|
|
const downloadPromise = page.waitForEvent('download');
|
|
await page.click('[data-testid="export-high-value-customers"]');
|
|
|
|
const download = await downloadPromise;
|
|
expect(download.suggestedFilename()).toMatch(/高价值客户.*\.xlsx$/);
|
|
});
|
|
|
|
test('客户流失分析应该正常工作', async ({ page }) => {
|
|
// 切换到流失分析标签
|
|
await page.click('[data-testid="tab-churn-analysis"]');
|
|
|
|
// 检查流失分析内容
|
|
await expect(page.locator('.churn-analysis')).toBeVisible();
|
|
await expect(page.locator('.churn-prediction')).toBeVisible();
|
|
await expect(page.locator('.churn-reasons')).toBeVisible();
|
|
|
|
// 检查流失预警列表
|
|
await expect(page.locator('.churn-alerts')).toBeVisible();
|
|
|
|
// 点击查看流失预警详情
|
|
const alertButton = page
|
|
.locator('.churn-alert-item')
|
|
.first()
|
|
.locator('[data-testid="view-alert"]');
|
|
if ((await alertButton.count()) > 0) {
|
|
await alertButton.click();
|
|
|
|
// 检查预警详情弹窗
|
|
await expect(page.locator('.ant-modal')).toBeVisible();
|
|
await expect(page.locator('.alert-details')).toBeVisible();
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('营销中心 - 效果统计', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto(TEST_CONFIG.baseURL);
|
|
await loginAsAdmin(page);
|
|
|
|
// 导航到效果统计页面
|
|
await page.click('[data-testid="menu-marketing"]');
|
|
await page.click('[data-testid="menu-performance-stats"]');
|
|
await page.waitForURL(/\/marketing\/performance/);
|
|
});
|
|
|
|
test('应该正确显示效果统计页面', async ({ page }) => {
|
|
// 检查页面标题
|
|
await expect(page.locator('.page-title')).toContainText(
|
|
/效果统计|营销效果/,
|
|
);
|
|
|
|
// 检查统计卡片
|
|
await expect(page.locator('.stats-cards')).toBeVisible();
|
|
await expect(
|
|
page.locator('[data-testid="total-impressions"]'),
|
|
).toBeVisible();
|
|
await expect(page.locator('[data-testid="total-clicks"]')).toBeVisible();
|
|
await expect(
|
|
page.locator('[data-testid="total-conversions"]'),
|
|
).toBeVisible();
|
|
await expect(page.locator('[data-testid="total-revenue"]')).toBeVisible();
|
|
|
|
// 检查趋势图表
|
|
await expect(page.locator('.trend-charts')).toBeVisible();
|
|
await expect(page.locator('.echarts-container')).toHaveCount({ min: 2 });
|
|
});
|
|
|
|
test('时间范围筛选应该正常工作', async ({ page }) => {
|
|
// 点击时间范围选择器
|
|
await page.click('[data-testid="date-range-picker"]');
|
|
|
|
// 选择最近7天
|
|
await page.click('[data-testid="preset-7days"]');
|
|
|
|
// 等待数据更新
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 检查数据是否更新
|
|
await expect(page.locator('.stats-cards')).toBeVisible();
|
|
|
|
// 选择自定义时间范围
|
|
await page.click('[data-testid="date-range-picker"]');
|
|
await page.click('[data-testid="custom-range"]');
|
|
|
|
// 选择开始日期
|
|
await page.click('.ant-picker-input:first-child');
|
|
await page.click('.ant-picker-today-btn');
|
|
|
|
// 选择结束日期
|
|
await page.click('.ant-picker-input:last-child');
|
|
await page.click('.ant-picker-today-btn');
|
|
|
|
// 确认选择
|
|
await page.click('.ant-picker-ok');
|
|
|
|
// 等待数据更新
|
|
await page.waitForTimeout(2000);
|
|
});
|
|
|
|
test('渠道对比分析应该正常工作', async ({ page }) => {
|
|
// 切换到渠道对比标签
|
|
await page.click('[data-testid="tab-channel-comparison"]');
|
|
|
|
// 检查渠道对比图表
|
|
await expect(page.locator('.channel-comparison')).toBeVisible();
|
|
await expect(page.locator('.channel-performance-table')).toBeVisible();
|
|
|
|
// 检查渠道效果表格
|
|
await expect(page.locator('.ant-table')).toBeVisible();
|
|
|
|
// 检查渠道排序功能
|
|
const channelHeader = page
|
|
.locator('.ant-table-thead th')
|
|
.filter({ hasText: /转化率|Conversion Rate/ });
|
|
await channelHeader.click();
|
|
|
|
// 等待排序完成
|
|
await page.waitForTimeout(1000);
|
|
|
|
// 检查排序指示器
|
|
await expect(
|
|
channelHeader.locator(
|
|
'.ant-table-column-sorter-up.active, .ant-table-column-sorter-down.active',
|
|
),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('活动效果对比应该正常工作', async ({ page }) => {
|
|
// 切换到活动对比标签
|
|
await page.click('[data-testid="tab-campaign-comparison"]');
|
|
|
|
// 检查活动对比内容
|
|
await expect(page.locator('.campaign-comparison')).toBeVisible();
|
|
|
|
// 选择要对比的活动
|
|
await page.click('[data-testid="select-campaigns"]');
|
|
await page.check('.campaign-option:first-child input');
|
|
await page.check('.campaign-option:nth-child(2) input');
|
|
await page.click('[data-testid="confirm-selection"]');
|
|
|
|
// 等待对比结果加载
|
|
await page.waitForTimeout(2000);
|
|
|
|
// 检查对比图表
|
|
await expect(page.locator('.comparison-chart')).toBeVisible();
|
|
await expect(page.locator('.comparison-table')).toBeVisible();
|
|
});
|
|
|
|
test('转化漏斗分析应该正常工作', async ({ page }) => {
|
|
// 切换到转化漏斗标签
|
|
await page.click('[data-testid="tab-conversion-funnel"]');
|
|
|
|
// 检查转化漏斗图
|
|
await expect(page.locator('.conversion-funnel')).toBeVisible();
|
|
await expect(page.locator('.funnel-chart')).toBeVisible();
|
|
|
|
// 检查漏斗各阶段数据
|
|
await expect(page.locator('.funnel-stage')).toHaveCount({ min: 3 });
|
|
|
|
// 点击漏斗阶段查看详情
|
|
const firstStage = page.locator('.funnel-stage').first();
|
|
await firstStage.click();
|
|
|
|
// 检查阶段详情
|
|
await expect(page.locator('.stage-details')).toBeVisible();
|
|
});
|
|
|
|
test('ROI分析应该正常工作', async ({ page }) => {
|
|
// 切换到ROI分析标签
|
|
await page.click('[data-testid="tab-roi-analysis"]');
|
|
|
|
// 检查ROI分析内容
|
|
await expect(page.locator('.roi-analysis')).toBeVisible();
|
|
await expect(page.locator('.roi-chart')).toBeVisible();
|
|
await expect(page.locator('.cost-revenue-comparison')).toBeVisible();
|
|
|
|
// 检查ROI计算器
|
|
await expect(page.locator('.roi-calculator')).toBeVisible();
|
|
|
|
// 使用ROI计算器
|
|
await page.fill('[data-testid="investment-amount"]', '10000');
|
|
await page.fill('[data-testid="revenue-amount"]', '15000');
|
|
await page.click('[data-testid="calculate-roi"]');
|
|
|
|
// 检查计算结果
|
|
await expect(page.locator('[data-testid="roi-result"]')).toContainText(
|
|
'50%',
|
|
);
|
|
});
|
|
|
|
test('报告导出功能应该正常工作', async ({ page }) => {
|
|
// 点击导出报告按钮
|
|
await page.click('[data-testid="export-report"]');
|
|
|
|
// 等待导出选项弹窗
|
|
await expect(page.locator('.ant-modal')).toBeVisible();
|
|
await expect(page.locator('.ant-modal-title')).toContainText(
|
|
/导出报告|Export Report/,
|
|
);
|
|
|
|
// 选择导出格式
|
|
await page.click('[data-testid="format-pdf"]');
|
|
|
|
// 选择导出内容
|
|
await page.check('[data-testid="include-charts"]');
|
|
await page.check('[data-testid="include-tables"]');
|
|
|
|
// 确认导出
|
|
const downloadPromise = page.waitForEvent('download');
|
|
await page.click('[data-testid="confirm-export"]');
|
|
|
|
const download = await downloadPromise;
|
|
expect(download.suggestedFilename()).toMatch(/营销效果报告.*\.pdf$/);
|
|
});
|
|
});
|
|
|
|
// 辅助函数:管理员登录
|
|
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 });
|
|
}
|