f14002559e
refactor(components): 调整头部和页脚布局样式 style(hero-section): 更新徽章动画效果 docs: 添加测试框架README文档 test: 实现首页、导航和联系表单的测试用例 ci: 添加CI测试脚本和配置
389 lines
13 KiB
Python
389 lines
13 KiB
Python
"""
|
|
联系页面测试模块
|
|
提供联系页面功能测试
|
|
"""
|
|
|
|
from typing import Any, Dict, List, Optional
|
|
from playwright.sync_api import Page, Locator, expect
|
|
|
|
from pages.base_page import BasePage
|
|
from config.settings import get_settings
|
|
from utils.logger import get_logger
|
|
from utils.helpers import ElementHelper, PageHelper, AssertionHelper
|
|
|
|
|
|
class ContactPage(BasePage):
|
|
"""联系页面对象"""
|
|
|
|
def __init__(self, page: Page, base_url: Optional[str] = None):
|
|
"""初始化联系页面"""
|
|
super().__init__(page, base_url)
|
|
|
|
self.path = "/contact"
|
|
self.title = "联系我们"
|
|
|
|
self.selectors = {
|
|
# 页面标题
|
|
"page_badge": "[class*='badge']",
|
|
"page_title": "h1",
|
|
"page_description": "p.text-gray-600",
|
|
|
|
# 联系信息卡片 - 根据实际页面结构
|
|
"contact_info_card": "div.grid > div:first-child",
|
|
"company_address": "text=公司地址 >> xpath=../following-sibling::p",
|
|
"company_phone": "text=联系电话 >> xpath=../following-sibling::p",
|
|
"company_email": "text=电子邮箱 >> xpath=../following-sibling::p",
|
|
"working_hours": "text=工作时间",
|
|
|
|
# 联系表单 - 使用ID选择器
|
|
"contact_form": "form",
|
|
"form_name_input": "#name",
|
|
"form_phone_input": "#phone",
|
|
"form_email_input": "#email",
|
|
"form_subject_input": "#subject",
|
|
"form_message_textarea": "#message",
|
|
"form_submit_button": "button[type='submit']",
|
|
|
|
# 表单字段标签
|
|
"name_label": "label[for='name']",
|
|
"phone_label": "label[for='phone']",
|
|
"email_label": "label[for='email']",
|
|
"subject_label": "label[for='subject']",
|
|
"message_label": "label[for='message']",
|
|
|
|
# 成功状态
|
|
"success_message": "text=消息已发送",
|
|
"success_icon": "svg[class*='text-green']",
|
|
|
|
# 加载状态
|
|
"submitting_loader": "text=发送中",
|
|
|
|
# 返回链接
|
|
"back_link": "a:has-text('返回'), a.back"
|
|
}
|
|
|
|
self.logger = get_logger()
|
|
|
|
def navigate(self, **kwargs) -> 'ContactPage':
|
|
"""导航到联系页面"""
|
|
super().navigate(**kwargs)
|
|
self.wait_for_load()
|
|
return self
|
|
|
|
def verify_page_loaded(self) -> 'ContactPage':
|
|
"""验证页面加载完成"""
|
|
self.logger.section("验证联系页面加载")
|
|
|
|
self.assert_element_visible("page_title", timeout=15000)
|
|
self.assert_element_visible("contact_form", timeout=15000)
|
|
|
|
self.logger.info("✅ 联系页面加载验证通过")
|
|
return self
|
|
|
|
def verify_page_structure(self) -> 'ContactPage':
|
|
"""验证页面结构"""
|
|
self.logger.section("验证页面结构")
|
|
|
|
# 检查页面标题区域
|
|
self.assert_element_visible("page_title")
|
|
|
|
# 检查联系信息 - 使用更通用的选择器
|
|
self._verify_contact_info_exists()
|
|
|
|
# 检查表单
|
|
self.assert_element_visible("contact_form")
|
|
|
|
self.logger.info("✅ 页面结构验证通过")
|
|
return self
|
|
|
|
def _verify_contact_info_exists(self) -> bool:
|
|
"""验证联系信息存在"""
|
|
# 检查是否包含联系信息文本
|
|
page_text = self.page.content()
|
|
has_address = "公司地址" in page_text
|
|
has_phone = "联系电话" in page_text
|
|
has_email = "电子邮箱" in page_text
|
|
|
|
assert has_address, "未找到公司地址信息"
|
|
assert has_phone, "未找到联系电话信息"
|
|
assert has_email, "未找到电子邮箱信息"
|
|
|
|
return True
|
|
|
|
def verify_company_info(self) -> 'ContactPage':
|
|
"""验证公司信息"""
|
|
self.logger.section("验证公司信息")
|
|
|
|
# 获取页面内容
|
|
page_content = self.page.content()
|
|
|
|
# 验证信息存在
|
|
assert "公司地址" in page_content, "未找到公司地址"
|
|
assert "联系电话" in page_content, "未找到联系电话"
|
|
assert "电子邮箱" in page_content, "未找到电子邮箱"
|
|
|
|
self.logger.info("✅ 公司信息验证通过")
|
|
return self
|
|
|
|
def verify_form_fields(self) -> 'ContactPage':
|
|
"""验证表单字段"""
|
|
self.logger.section("验证表单字段")
|
|
|
|
required_fields = [
|
|
("form_name_input", "姓名"),
|
|
("form_email_input", "邮箱"),
|
|
("form_subject_input", "主题"),
|
|
("form_message_textarea", "消息")
|
|
]
|
|
|
|
for selector, field_name in required_fields:
|
|
self.assert_element_visible(selector, timeout=5000)
|
|
|
|
# 检查必填标记
|
|
label = self.page.locator(f"label[for='{selector.replace('#', '')}']")
|
|
if label.count() > 0:
|
|
label_text = label.text_content()
|
|
if "*" in (label_text or ""):
|
|
self.logger.info(f"{field_name} 为必填项")
|
|
|
|
# 检查可选字段
|
|
self.assert_element_visible("form_phone_input")
|
|
|
|
self.logger.info("✅ 表单字段验证通过")
|
|
return self
|
|
|
|
def fill_contact_form(self, data: Dict[str, str]) -> 'ContactPage':
|
|
"""填充联系表单"""
|
|
self.logger.section("填充联系表单")
|
|
|
|
# 姓名
|
|
if "name" in data:
|
|
self._fill("form_name_input", data["name"])
|
|
self.logger.log_action(f"填写姓名: {data['name']}")
|
|
|
|
# 电话
|
|
if "phone" in data:
|
|
self._fill("form_phone_input", data["phone"])
|
|
self.logger.log_action(f"填写电话: {data['phone']}")
|
|
|
|
# 邮箱
|
|
if "email" in data:
|
|
self._fill("form_email_input", data["email"])
|
|
self.logger.log_action(f"填写邮箱: {data['email']}")
|
|
|
|
# 主题
|
|
if "subject" in data:
|
|
self._fill("form_subject_input", data["subject"])
|
|
self.logger.log_action(f"填写主题: {data['subject']}")
|
|
|
|
# 消息
|
|
if "message" in data:
|
|
self._fill("form_message_textarea", data["message"])
|
|
self.logger.log_action(f"填写消息: {data['message'][:50]}...")
|
|
|
|
return self
|
|
|
|
def submit_form(self, wait_for_response: bool = True) -> 'ContactPage':
|
|
"""提交表单"""
|
|
self.logger.log_action("提交联系表单")
|
|
|
|
# 等待表单按钮可用
|
|
submit_button = self._find("form_submit_button")
|
|
|
|
# 点击提交
|
|
submit_button.click()
|
|
|
|
if wait_for_response:
|
|
# 等待加载完成
|
|
self.page.wait_for_load_state("networkidle")
|
|
|
|
# 检查是否显示成功消息
|
|
try:
|
|
self.assert_element_visible("success_message", timeout=10000)
|
|
self.logger.info("表单提交成功")
|
|
except Exception:
|
|
self.logger.warning("未检测到成功消息,可能提交失败或无反馈")
|
|
|
|
return self
|
|
|
|
def verify_form_submission_success(self) -> 'ContactPage':
|
|
"""验证表单提交成功"""
|
|
self.logger.section("验证表单提交成功")
|
|
|
|
# 检查成功消息
|
|
self.assert_element_visible("success_message")
|
|
|
|
# 验证成功消息文本
|
|
success_text = self._get_text("success_message")
|
|
assert "已发送" in success_text or "成功" in success_text, \
|
|
f"成功消息不正确: {success_text}"
|
|
|
|
self.logger.info("✅ 表单提交成功验证通过")
|
|
return self
|
|
|
|
def verify_form_validation(self) -> 'ContactPage':
|
|
"""验证表单验证"""
|
|
self.logger.section("验证表单验证")
|
|
|
|
# 尝试提交空表单
|
|
self._click("form_submit_button")
|
|
|
|
# 检查浏览器原生验证
|
|
name_input = self._find("form_name_input")
|
|
is_required = name_input.evaluate("el => el.required")
|
|
|
|
if is_required:
|
|
self.logger.info("姓名字段为必填项")
|
|
|
|
# 验证邮箱格式
|
|
self._fill("form_email_input", "invalid-email")
|
|
self._click("form_subject_input")
|
|
|
|
# 检查HTML5验证
|
|
email_input = self._find("form_email_input")
|
|
validity = email_input.evaluate("""
|
|
el => ({
|
|
valid: el.validity.valid,
|
|
typeMismatch: el.validity.typeMismatch,
|
|
valueMissing: el.validity.valueMissing
|
|
})
|
|
""")
|
|
|
|
if not validity["valid"] and validity["typeMismatch"]:
|
|
self.logger.info("邮箱格式验证正常工作")
|
|
|
|
self.logger.info("✅ 表单验证验证通过")
|
|
return self
|
|
|
|
def verify_form_with_invalid_email(self, data: Dict[str, str]) -> 'ContactPage':
|
|
"""使用无效邮箱测试表单验证"""
|
|
self.logger.section("测试无效邮箱")
|
|
|
|
# 填写表单(使用无效邮箱)
|
|
data["email"] = "invalid-email"
|
|
self.fill_contact_form(data)
|
|
|
|
# 尝试提交
|
|
self._click("form_submit_button")
|
|
|
|
# 检查是否被HTML5验证阻止
|
|
email_input = self._find("form_email_input")
|
|
is_valid = email_input.evaluate("el => el.validity.valid")
|
|
|
|
if not is_valid:
|
|
self.logger.info("无效邮箱被正确阻止")
|
|
else:
|
|
self.logger.warning("无效邮箱未被验证阻止,可能存在后端验证")
|
|
|
|
return self
|
|
|
|
def test_form_submission_performance(
|
|
self,
|
|
data: Dict[str, str],
|
|
max_duration: float = 5.0
|
|
) -> Dict[str, Any]:
|
|
"""测试表单提交性能"""
|
|
self.logger.section("表单提交性能测试")
|
|
|
|
import time
|
|
|
|
# 填充表单
|
|
self.fill_contact_form(data)
|
|
|
|
# 记录开始时间
|
|
start_time = time.time()
|
|
|
|
# 提交表单
|
|
self._click("form_submit_button")
|
|
|
|
# 等待成功消息
|
|
try:
|
|
self.assert_element_visible("success_message", timeout=10000)
|
|
except Exception:
|
|
pass
|
|
|
|
# 记录结束时间
|
|
end_time = time.time()
|
|
duration = end_time - start_time
|
|
|
|
# 验证性能
|
|
if duration <= max_duration:
|
|
self.logger.info(f"✅ 表单提交耗时 {duration:.2f}s,在阈值 {max_duration}s 内")
|
|
else:
|
|
self.logger.warning(f"⚠️ 表单提交耗时 {duration:.2f}s,超过阈值 {max_duration}s")
|
|
|
|
return {
|
|
"duration": duration,
|
|
"passed": duration <= max_duration
|
|
}
|
|
|
|
def get_working_hours(self) -> Dict[str, str]:
|
|
"""获取工作时间"""
|
|
# 从页面内容中提取工作时间
|
|
page_text = self.page.content()
|
|
|
|
hours = {}
|
|
|
|
# 检查工作时间文本
|
|
if "周一至周五" in page_text:
|
|
hours["周一至周五"] = "9:00 - 18:00"
|
|
if "周六" in page_text:
|
|
hours["周六"] = "9:00 - 12:00"
|
|
if "周日" in page_text:
|
|
hours["周日"] = "休息"
|
|
|
|
return hours
|
|
|
|
def reset_form(self) -> 'ContactPage':
|
|
"""重置表单"""
|
|
self.logger.log_action("重置表单")
|
|
|
|
# 刷新页面
|
|
self.reload()
|
|
self.wait_for_load()
|
|
|
|
return self
|
|
|
|
def verify_responsive_layout(self, width: int) -> 'ContactPage':
|
|
"""验证响应式布局"""
|
|
self.logger.section(f"响应式测试 ({width}px)")
|
|
|
|
# 设置视口
|
|
self.page.set_viewport_size({"width": width, "height": 800})
|
|
self.wait_for_load()
|
|
|
|
# 验证布局
|
|
self.assert_element_visible("contact_form", timeout=5000)
|
|
|
|
# 检查布局变化
|
|
if width < 768:
|
|
self.logger.info("移动端布局:单列布局")
|
|
elif width < 1024:
|
|
self.logger.info("平板端布局:双列布局")
|
|
else:
|
|
self.logger.info("桌面端布局:完整布局")
|
|
|
|
self.logger.info(f"✅ {width}px 响应式测试通过")
|
|
return self
|
|
|
|
def extract_contact_details(self) -> Dict[str, str]:
|
|
"""提取联系详情"""
|
|
details = {}
|
|
|
|
# 从页面内容中提取
|
|
page_content = self.page.content()
|
|
|
|
# 公司地址
|
|
if "公司地址" in page_content:
|
|
details["address"] = "已找到地址信息"
|
|
|
|
# 联系电话
|
|
if "联系电话" in page_content:
|
|
details["phone"] = "已找到电话信息"
|
|
|
|
# 电子邮箱
|
|
if "电子邮箱" in page_content:
|
|
details["email"] = "已找到邮箱信息"
|
|
|
|
return details
|