feat(e2e-tests): 添加端到端测试框架及测试用例
refactor(components): 调整头部和页脚布局样式 style(hero-section): 更新徽章动画效果 docs: 添加测试框架README文档 test: 实现首页、导航和联系表单的测试用例 ci: 添加CI测试脚本和配置
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
"""
|
||||
性能测试模块
|
||||
测试网站性能指标
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
from typing import Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
from pages.home_page import HomePage
|
||||
from pages.contact_page import ContactPage
|
||||
from config.settings import get_settings
|
||||
|
||||
|
||||
class TestPerformance:
|
||||
"""性能测试类"""
|
||||
|
||||
@pytest.mark.performance
|
||||
@pytest.mark.smoke
|
||||
def test_home_page_load_time(self, home_page: HomePage):
|
||||
"""测试首页加载时间"""
|
||||
home_page.navigate()
|
||||
|
||||
start_time = time.time()
|
||||
home_page.wait_for_load()
|
||||
end_time = time.time()
|
||||
|
||||
load_time = (end_time - start_time) * 1000 # 毫秒
|
||||
|
||||
# 阈值:5秒
|
||||
assert load_time < 5000, f"首页加载时间 {load_time:.2f}ms 超过5秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
@pytest.mark.smoke
|
||||
def test_contact_page_load_time(self, contact_page: ContactPage):
|
||||
"""测试联系页面加载时间"""
|
||||
contact_page.navigate()
|
||||
|
||||
start_time = time.time()
|
||||
contact_page.wait_for_load()
|
||||
end_time = time.time()
|
||||
|
||||
load_time = (end_time - start_time) * 1000
|
||||
|
||||
# 阈值:5秒
|
||||
assert load_time < 5000, f"联系页面加载时间 {load_time:.2f}ms 超过5秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_dom_content_loaded_time(self, home_page: HomePage):
|
||||
"""测试DOM内容加载时间"""
|
||||
home_page.navigate()
|
||||
|
||||
performance_data = home_page.execute_js("""
|
||||
() => {
|
||||
const timing = performance.timing;
|
||||
return {
|
||||
domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart,
|
||||
domInteractive: timing.domInteractive - timing.domLoading
|
||||
};
|
||||
}
|
||||
""")
|
||||
|
||||
dom_loaded = performance_data.get("domContentLoaded", 0)
|
||||
assert dom_loaded < 3000, f"DOM内容加载时间 {dom_loaded}ms 超过3秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_first_paint_time(self, home_page: HomePage):
|
||||
"""测试首次绘制时间"""
|
||||
home_page.navigate()
|
||||
|
||||
first_paint = home_page.execute_js("""
|
||||
() => {
|
||||
const entries = performance.getEntriesByType('paint');
|
||||
const firstPaint = entries.find(e => e.name === 'first-paint');
|
||||
return firstPaint ? firstPaint.startTime : 0;
|
||||
}
|
||||
""")
|
||||
|
||||
if first_paint:
|
||||
assert first_paint < 2000, f"首次绘制时间 {first_paint:.2f}ms 超过2秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_first_contentful_paint(self, home_page: HomePage):
|
||||
"""测试首次内容绘制(FCP)"""
|
||||
home_page.navigate()
|
||||
|
||||
fcp = home_page.execute_js("""
|
||||
() => {
|
||||
const navigation = performance.getEntriesByType('navigation')[0];
|
||||
return navigation ? navigation.firstContentfulPaint : 0;
|
||||
}
|
||||
""")
|
||||
|
||||
if fcp:
|
||||
# 阈值:1.5秒
|
||||
assert fcp < 1500, f"首次内容绘制时间 {fcp:.2f}ms 超过1.5秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_largest_contentful_paint(self, home_page: HomePage):
|
||||
"""测试最大内容绘制(LCP)"""
|
||||
home_page.navigate()
|
||||
|
||||
lcp = home_page.execute_js("""
|
||||
() => {
|
||||
try {
|
||||
const navigation = performance.getEntriesByType('navigation')[0];
|
||||
return navigation ? navigation.largestContentfulPaint : 0;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
if lcp:
|
||||
# 阈值:2.5秒
|
||||
assert lcp < 2500, f"最大内容绘制时间 {lcp:.2f}ms 超过2.5秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_time_to_interactive(self, home_page: HomePage):
|
||||
"""测试可交互时间(TTI)"""
|
||||
home_page.navigate()
|
||||
|
||||
tti = home_page.execute_js("""
|
||||
() => {
|
||||
try {
|
||||
const navigation = performance.getEntriesByType('navigation')[0];
|
||||
return navigation ? navigation.interactive : 0;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
if tti:
|
||||
# 阈值:3秒
|
||||
assert tti < 3000, f"可交互时间 {tti:.2f}ms 超过3秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_page_load_performance_metrics(self, home_page: HomePage):
|
||||
"""测试页面加载性能指标"""
|
||||
home_page.navigate()
|
||||
performance = home_page.verify_page_performance()
|
||||
|
||||
# 验证关键指标
|
||||
if performance.get("pageLoadTime"):
|
||||
assert performance["pageLoadTime"] < 5000
|
||||
if performance.get("domContentLoaded"):
|
||||
assert performance["domContentLoaded"] < 3000
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_network_timing(self, home_page: HomePage):
|
||||
"""测试网络时序"""
|
||||
home_page.navigate()
|
||||
|
||||
timing = home_page.execute_js("""
|
||||
() => {
|
||||
const timing = performance.timing;
|
||||
return {
|
||||
dnsLookup: timing.domainLookupEnd - timing.domainLookupStart,
|
||||
tcpConnection: timing.connectEnd - timing.connectStart,
|
||||
serverResponse: timing.responseEnd - timing.requestStart,
|
||||
domProcessing: timing.domLoading - timing.responseEnd
|
||||
};
|
||||
}
|
||||
""")
|
||||
|
||||
# DNS查询时间
|
||||
assert timing.get("dnsLookup", 0) < 500, \
|
||||
f"DNS查询时间 {timing.get('dnsLookup')}ms 超过500ms阈值"
|
||||
|
||||
# TCP连接时间
|
||||
assert timing.get("tcpConnection", 0) < 500, \
|
||||
f"TCP连接时间 {timing.get('tcpConnection')}ms 超过500ms阈值"
|
||||
|
||||
# 服务器响应时间
|
||||
assert timing.get("serverResponse", 0) < 1000, \
|
||||
f"服务器响应时间 {timing.get('serverResponse')}ms 超过1秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_resource_timing(self, home_page: HomePage):
|
||||
"""测试资源加载时序"""
|
||||
home_page.navigate()
|
||||
|
||||
resources = home_page.execute_js("""
|
||||
() => {
|
||||
const entries = performance.getEntriesByType('resource');
|
||||
const scripts = entries.filter(e => e.initiatorType === 'script');
|
||||
const styles = entries.filter(e => e.initiatorType === 'css');
|
||||
|
||||
return {
|
||||
totalResources: entries.length,
|
||||
scriptCount: scripts.length,
|
||||
styleCount: styles.length,
|
||||
totalDuration: entries.reduce((sum, e) => sum + e.duration, 0)
|
||||
};
|
||||
}
|
||||
""")
|
||||
|
||||
assert resources.get("totalResources", 0) > 0, "未检测到资源加载"
|
||||
|
||||
home_page.logger.info(
|
||||
f"资源统计: 共{resources.get('totalResources')}个资源,"
|
||||
f"脚本{resources.get('scriptCount')}个,"
|
||||
f"样式{resources.get('styleCount')}个"
|
||||
)
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_form_submission_time(self, contact_page: ContactPage, test_data_generator):
|
||||
"""测试表单提交时间"""
|
||||
contact_page.navigate()
|
||||
|
||||
data = test_data_generator.generate_contact_form_data(use_valid=True)
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
contact_page.fill_contact_form(data)
|
||||
contact_page.submit_form()
|
||||
|
||||
try:
|
||||
contact_page.verify_form_submission_success()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
end_time = time.time()
|
||||
duration = (end_time - start_time) * 1000
|
||||
|
||||
# 阈值:5秒
|
||||
assert duration < 5000, f"表单提交耗时 {duration:.2f}ms 超过5秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_scroll_performance(self, home_page: HomePage):
|
||||
"""测试滚动性能"""
|
||||
home_page.navigate()
|
||||
|
||||
# 执行多次滚动
|
||||
scroll_times = []
|
||||
for i in range(5):
|
||||
start_time = time.time()
|
||||
home_page.scroll_to_bottom()
|
||||
home_page.scroll_to_top()
|
||||
end_time = time.time()
|
||||
scroll_times.append((end_time - start_time) * 1000)
|
||||
|
||||
avg_scroll_time = sum(scroll_times) / len(scroll_times)
|
||||
|
||||
# 平均滚动时间应该在1秒内
|
||||
assert avg_scroll_time < 1000, f"平均滚动时间 {avg_scroll_time:.2f}ms 超过1秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_element_visibility_performance(self, home_page: HomePage):
|
||||
"""测试元素可见性检查性能"""
|
||||
home_page.navigate()
|
||||
|
||||
elements = [
|
||||
"header",
|
||||
"#home",
|
||||
"#about",
|
||||
"#services",
|
||||
"#products",
|
||||
"#news",
|
||||
"#contact",
|
||||
"footer"
|
||||
]
|
||||
|
||||
check_times = []
|
||||
for element in elements:
|
||||
start_time = time.time()
|
||||
try:
|
||||
home_page._is_visible(element)
|
||||
except Exception:
|
||||
pass
|
||||
end_time = time.time()
|
||||
check_times.append((end_time - start_time) * 1000)
|
||||
|
||||
avg_check_time = sum(check_times) / len(check_times)
|
||||
|
||||
# 单个元素检查时间应该在500ms内
|
||||
assert avg_check_time < 500, f"平均元素检查时间 {avg_check_time:.2f}ms 超过500ms阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
def test_navigation_performance(self, home_page: HomePage, contact_page: ContactPage):
|
||||
"""测试导航性能"""
|
||||
# 测量导航到联系页面的时间
|
||||
start_time = time.time()
|
||||
contact_page.navigate()
|
||||
contact_page.wait_for_load()
|
||||
end_time = time.time()
|
||||
|
||||
nav_time = (end_time - start_time) * 1000
|
||||
|
||||
# 阈值:3秒
|
||||
assert nav_time < 3000, f"导航时间 {nav_time:.2f}ms 超过3秒阈值"
|
||||
|
||||
@pytest.mark.performance
|
||||
@pytest.mark.responsive
|
||||
def test_performance_across_viewports(self, home_page: HomePage):
|
||||
"""测试不同视口下的性能"""
|
||||
viewports = [
|
||||
(375, 667, "移动端"),
|
||||
(768, 1024, "平板端"),
|
||||
(1920, 1080, "桌面端")
|
||||
]
|
||||
|
||||
results = []
|
||||
for width, height, name in viewports:
|
||||
home_page.page.set_viewport_size({"width": width, "height": height})
|
||||
|
||||
start_time = time.time()
|
||||
home_page.navigate()
|
||||
home_page.wait_for_load()
|
||||
end_time = time.time()
|
||||
|
||||
load_time = (end_time - start_time) * 1000
|
||||
results.append((name, load_time))
|
||||
|
||||
home_page.logger.info(f"{name} ({width}x{height}): {load_time:.2f}ms")
|
||||
|
||||
# 验证所有视口加载时间在阈值内
|
||||
for name, load_time in results:
|
||||
assert load_time < 5000, f"{name}加载时间 {load_time:.2f}ms 超过5秒阈值"
|
||||
Reference in New Issue
Block a user