293 lines
9.8 KiB
TypeScript
293 lines
9.8 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
||
|
||
/**
|
||
* 🖱️ 蚊子项目H5前端 - 用户操作测试
|
||
* 模拟真实用户在H5界面的查看和操作
|
||
*
|
||
* 注意:H5_BASE_URL必须通过环境变量配置,默认值仅供参考
|
||
* 使用方式: H5_BASE_URL=http://localhost:5173 npx playwright test
|
||
*
|
||
* 注意:H5与Admin共享同一前端服务(5173),H5路由(/share等)由Admin前端提供
|
||
*/
|
||
|
||
test.describe('👤 用户H5前端操作测试', () => {
|
||
|
||
const FRONTEND_URL = process.env.H5_BASE_URL || 'http://localhost:5176';
|
||
const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:8080';
|
||
|
||
/**
|
||
* 验证当前页面是否为H5应用(而非管理后台)
|
||
* 通过检查页面特征来识别
|
||
*/
|
||
const verifyH5Page = async (page: any, expectedPath?: string) => {
|
||
const currentUrl = page.url();
|
||
|
||
// 如果指定了期望路径,验证路径
|
||
if (expectedPath && !currentUrl.includes(expectedPath)) {
|
||
throw new Error(`页面路径不正确: ${currentUrl},期望包含: ${expectedPath}`);
|
||
}
|
||
|
||
// H5页面特征:路径是 / 或 /share 或 /rank 或 /profile
|
||
const isH5Path = currentUrl === '/' ||
|
||
currentUrl.includes('/share') ||
|
||
currentUrl.includes('/rank') ||
|
||
currentUrl.includes('/profile');
|
||
|
||
// 如果不是H5路径,抛出错误
|
||
if (!isH5Path && !currentUrl.includes('localhost')) {
|
||
throw new Error(`E2E目标偏离:当前页面 ${currentUrl} 不是H5应用,可能跑到了管理前端或其他应用`);
|
||
}
|
||
|
||
console.log(` ✅ H5页面验证通过: ${currentUrl}`);
|
||
return true;
|
||
};
|
||
|
||
test('📱 查看首页和底部导航', async ({ page }) => {
|
||
await test.step('访问H5首页', async () => {
|
||
// 访问首页
|
||
const response = await page.goto(FRONTEND_URL);
|
||
|
||
// 验证页面可访问
|
||
expect(response).not.toBeNull();
|
||
console.log(' ✅ 首页响应状态:', response?.status());
|
||
|
||
// 等待页面加载完成
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// 验证是H5页面而非管理后台
|
||
await verifyH5Page(page, '/');
|
||
|
||
// 截图记录首页
|
||
await page.screenshot({
|
||
path: 'test-results/h5-user-homepage.png',
|
||
fullPage: true
|
||
});
|
||
console.log(' 📸 首页截图已保存');
|
||
});
|
||
|
||
await test.step('检查底部导航栏', async () => {
|
||
// 查找导航栏
|
||
const nav = page.locator('nav');
|
||
const navExists = await nav.count() > 0;
|
||
|
||
if (navExists) {
|
||
console.log(' ✅ 底部导航栏已找到');
|
||
|
||
// 查找导航链接
|
||
const homeLink = page.locator('text=首页').first();
|
||
const shareLink = page.locator('text=推广').first();
|
||
const rankLink = page.locator('text=排行').first();
|
||
|
||
// 验证导航项存在
|
||
const hasHome = await homeLink.count() > 0;
|
||
const hasShare = await shareLink.count() > 0;
|
||
const hasRank = await rankLink.count() > 0;
|
||
|
||
console.log(` 📊 导航项: 首页(${hasHome ? '✓' : '✗'}), 推广(${hasShare ? '✓' : '✗'}), 排行(${hasRank ? '✓' : '✗'})`);
|
||
} else {
|
||
console.log(' ⚠️ 未找到底部导航栏');
|
||
}
|
||
});
|
||
});
|
||
|
||
test('🖱️ 用户点击导航菜单', async ({ page }) => {
|
||
await test.step('点击推广页面', async () => {
|
||
await page.goto(FRONTEND_URL);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// 验证是H5页面
|
||
await verifyH5Page(page);
|
||
|
||
// 查找并点击推广链接
|
||
const shareLink = page.locator('text=推广').first();
|
||
|
||
if (await shareLink.count() > 0) {
|
||
console.log(' 🖱️ 点击推广导航项');
|
||
await shareLink.click();
|
||
|
||
// 等待页面切换
|
||
await page.waitForTimeout(1000);
|
||
|
||
// 截图记录推广页面
|
||
await page.screenshot({
|
||
path: 'test-results/h5-user-share-page.png',
|
||
fullPage: true
|
||
});
|
||
console.log(' ✅ 推广页面截图已保存');
|
||
|
||
// 验证URL变化
|
||
const currentUrl = page.url();
|
||
console.log(' 🔗 当前URL:', currentUrl);
|
||
} else {
|
||
console.log(' ⚠️ 未找到推广导航项');
|
||
}
|
||
});
|
||
|
||
await test.step('点击排行榜页面', async () => {
|
||
// 查找并点击排行链接
|
||
const rankLink = page.locator('text=排行').first();
|
||
|
||
if (await rankLink.count() > 0) {
|
||
console.log(' 🖱️ 点击排行导航项');
|
||
await rankLink.click();
|
||
|
||
// 等待页面切换
|
||
await page.waitForTimeout(1000);
|
||
|
||
// 截图记录排行榜页面
|
||
await page.screenshot({
|
||
path: 'test-results/h5-user-rank-page.png',
|
||
fullPage: true
|
||
});
|
||
console.log(' ✅ 排行榜页面截图已保存');
|
||
|
||
// 验证URL变化
|
||
const currentUrl = page.url();
|
||
console.log(' 🔗 当前URL:', currentUrl);
|
||
} else {
|
||
console.log(' ⚠️ 未找到排行导航项');
|
||
}
|
||
});
|
||
|
||
await test.step('返回首页', async () => {
|
||
// 查找并点击首页链接
|
||
const homeLink = page.locator('text=首页').first();
|
||
|
||
if (await homeLink.count() > 0) {
|
||
console.log(' 🖱️ 点击首页导航项');
|
||
await homeLink.click();
|
||
|
||
// 等待页面切换
|
||
await page.waitForTimeout(1000);
|
||
|
||
// 验证返回首页
|
||
const currentUrl = page.url();
|
||
console.log(' 🔗 返回首页URL:', currentUrl);
|
||
}
|
||
});
|
||
});
|
||
|
||
test('📱 移动端响应式布局测试', async ({ page }) => {
|
||
const viewports = [
|
||
{ width: 375, height: 667, name: 'iPhone-SE' },
|
||
{ width: 414, height: 896, name: 'iPhone-12-Pro' },
|
||
{ width: 768, height: 1024, name: 'iPad' }
|
||
];
|
||
|
||
for (const viewport of viewports) {
|
||
await test.step(`${viewport.name}设备布局检查`, async () => {
|
||
// 设置设备尺寸
|
||
await page.setViewportSize({
|
||
width: viewport.width,
|
||
height: viewport.height
|
||
});
|
||
|
||
await page.goto(FRONTEND_URL);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// 验证是H5页面
|
||
await verifyH5Page(page);
|
||
|
||
// 截图记录不同设备效果
|
||
await page.screenshot({
|
||
path: `test-results/h5-responsive-${viewport.name}.png`,
|
||
fullPage: true
|
||
});
|
||
|
||
console.log(` 📱 ${viewport.name} (${viewport.width}x${viewport.height}) 截图完成`);
|
||
|
||
// 验证底部导航在移动端可见
|
||
const nav = page.locator('nav');
|
||
const isNavVisible = await nav.isVisible().catch(() => false);
|
||
console.log(` ${isNavVisible ? '✅' : '⚠️'} 底部导航栏可见性: ${isNavVisible}`);
|
||
});
|
||
}
|
||
});
|
||
|
||
test('🔍 页面元素检查和交互', async ({ page }) => {
|
||
await test.step('检查页面元素', async () => {
|
||
await page.goto(FRONTEND_URL);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// 验证是H5页面
|
||
await verifyH5Page(page);
|
||
|
||
// 统计页面元素
|
||
const buttons = page.locator('button');
|
||
const links = page.locator('a');
|
||
const images = page.locator('img');
|
||
const inputs = page.locator('input');
|
||
|
||
const buttonCount = await buttons.count();
|
||
const linkCount = await links.count();
|
||
const imageCount = await images.count();
|
||
const inputCount = await inputs.count();
|
||
|
||
console.log(' 📊 页面元素统计:');
|
||
console.log(` - 按钮: ${buttonCount} 个`);
|
||
console.log(` - 链接: ${linkCount} 个`);
|
||
console.log(` - 图片: ${imageCount} 个`);
|
||
console.log(` - 输入框: ${inputCount} 个`);
|
||
|
||
// 如果存在按钮,测试点击第一个
|
||
if (buttonCount > 0) {
|
||
const firstButton = buttons.first();
|
||
const buttonText = await firstButton.textContent();
|
||
console.log(` 🖱️ 第一个按钮: "${buttonText}"`);
|
||
}
|
||
|
||
// 获取页面完整文本内容预览
|
||
const pageText = await page.textContent('body');
|
||
if (pageText) {
|
||
const preview = pageText.replace(/\s+/g, ' ').substring(0, 200);
|
||
console.log(` 📝 页面内容: ${preview}...`);
|
||
}
|
||
});
|
||
});
|
||
|
||
test('⏱️ 页面性能测试', async ({ page }) => {
|
||
await test.step('测量页面加载性能', async () => {
|
||
const startTime = Date.now();
|
||
|
||
await page.goto(FRONTEND_URL);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// 验证是H5页面
|
||
await verifyH5Page(page);
|
||
|
||
const loadTime = Date.now() - startTime;
|
||
console.log(` ⏱️ 页面加载时间: ${loadTime}ms`);
|
||
|
||
// 验证加载时间
|
||
expect(loadTime).toBeLessThan(10000); // 10秒内加载完成
|
||
|
||
// 获取性能指标
|
||
const performanceMetrics = await page.evaluate(() => {
|
||
const timing = performance.timing;
|
||
return {
|
||
domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart,
|
||
loadComplete: timing.loadEventEnd - timing.navigationStart,
|
||
};
|
||
});
|
||
|
||
console.log(' 📊 性能指标:');
|
||
console.log(` - DOM内容加载: ${performanceMetrics.domContentLoaded}ms`);
|
||
console.log(` - 页面完全加载: ${performanceMetrics.loadComplete}ms`);
|
||
});
|
||
});
|
||
|
||
test('🔗 前后端连通性测试', async ({ request }) => {
|
||
await test.step('验证后端API可用', async () => {
|
||
const response = await request.get(`${API_BASE_URL}/actuator/health`);
|
||
|
||
expect(response.status()).toBe(200);
|
||
|
||
const body = await response.json();
|
||
expect(body.status).toBe('UP');
|
||
|
||
console.log(' ✅ 后端API连通性正常');
|
||
console.log(' 📊 后端状态:', JSON.stringify(body, null, 2));
|
||
});
|
||
});
|
||
});
|