""" 首页测试模块 提供首页功能测试 """ from typing import Any, Dict, List, Optional from playwright.sync_api import Page, Locator from pages.base_page import BasePage from config.settings import get_settings from utils.logger import get_logger class HomePage(BasePage): """首页页面对象""" def __init__(self, page: Page, base_url: Optional[str] = None): """初始化首页""" super().__init__(page, base_url) self.path = "/" self.title = "四川睿新致远科技有限公司" self.selectors = { # 导航相关 "header": "header", "logo": "header img[alt*='logo'], header a[href='/']", "navigation": "header nav, nav", "nav_links": "nav a, header nav a", # Hero区域 - 使用实际的 section ID "hero_section": "#home, section:first-of-type", "hero_title": "h1", "hero_subtitle": "#home p, section:first-of-type p", "hero_cta": "a[href*='/contact'], a:has-text('立即咨询')", # 关于我们区域 - 使用文本匹配 "about_section": "#about, section:has(h2:has-text('关于'))", "about_title": "#about h2, h2:has-text('关于')", "about_content": "#about .content", # 核心业务区域 "services_section": "#services, section:has(h2:has-text('业务')), section:has(h2:has-text('服务'))", "services_title": "#services h2, h2:has-text('业务'), h2:has-text('服务')", "services_cards": "#services .card, .service-card", # 产品服务区域 "products_section": "#products, section:has(h2:has-text('产品'))", "products_title": "#products h2, h2:has-text('产品')", "products_grid": "#products .grid", "product_cards": "#products .card, .product-card", # 新闻动态区域 "news_section": "#news, section:has(h2:has-text('新闻')), section:has(h2:has-text('动态'))", "news_title": "#news h2, h2:has-text('新闻'), h2:has-text('动态')", "news_list": "#news .list, .news-list", "news_items": "#news .news-item, .news-item", # 联系我们区域 - 使用 /contact 页面 "contact_section": "#contact, section:has(h2:has-text('联系')), section:has(h2:has-text('联系方式'))", "contact_title": "#contact h2, h2:has-text('联系'), h2:has-text('联系方式')", "contact_form": "form", # 页脚 "footer": "footer", "footer_content": "footer .content, footer .footer-content", "social_links": "footer .social-links, footer a[href*='weixin'], footer a[href*='weibo']" } self.logger = get_logger() def navigate(self, **kwargs) -> 'HomePage': """导航到首页""" super().navigate(**kwargs) self.wait_for_load() return self def verify_page_loaded(self) -> 'HomePage': """验证页面加载完成""" self.logger.section("验证首页加载") # 检查关键元素存在 self.assert_element_visible("header", timeout=10000) self.assert_element_visible("main", timeout=10000) self.assert_element_visible("footer", timeout=10000) # 检查页面标题 self.assert_title_contains("睿新致远") self.logger.info("✅ 首页加载验证通过") return self def verify_header(self) -> 'HomePage': """验证页头""" self.logger.section("验证页头") # 检查Logo if self._is_visible("logo"): self.logger.info("Logo存在") # 检查导航链接 - 桌面端和移动端各有导航项 nav_links = self._find_all("nav_links") min_expected = 6 # 至少6个导航项 assert len(nav_links) >= min_expected, f"导航链接数量不足: 预期至少{min_expected}个,实际{len(nav_links)}个" self.logger.info(f"✅ 页头验证通过,发现 {len(nav_links)} 个导航链接") return self def verify_hero_section(self) -> 'HomePage': """验证Hero区域""" self.logger.section("验证Hero区域") if self._is_visible("hero_section"): self.assert_element_visible("hero_title") self.assert_element_visible("hero_subtitle") self.logger.info("Hero区域完整") # 获取标题文本 title = self._get_text("hero_title") self.logger.info(f"Hero标题: {title[:50]}...") else: self.logger.warning("未找到Hero区域") return self def verify_services_section(self) -> 'HomePage': """验证核心业务区域""" self.logger.section("验证核心业务区域") if self._is_visible("services_section"): self.assert_element_visible("services_title") # 检查业务卡片 cards = self._find_all("services_cards") self.logger.info(f"发现 {len(cards)} 个服务卡片") if len(cards) > 0: self.logger.info("✅ 服务区域验证通过") else: self.logger.warning("未找到服务区域") return self def verify_products_section(self) -> 'HomePage': """验证产品服务区域""" self.logger.section("验证产品服务区域") if self._is_visible("products_section"): self.assert_element_visible("products_title") # 检查产品卡片 cards = self._find_all("product_cards") self.logger.info(f"发现 {len(cards)} 个产品卡片") if len(cards) > 0: self.logger.info("✅ 产品区域验证通过") else: self.logger.warning("未找到产品区域") return self def verify_news_section(self) -> 'HomePage': """验证新闻动态区域""" self.logger.section("验证新闻动态区域") if self._is_visible("news_section"): self.assert_element_visible("news_title") # 检查新闻列表 items = self._find_all("news_items") self.logger.info(f"发现 {len(items)} 条新闻") if len(items) > 0: self.logger.info("✅ 新闻区域验证通过") else: self.logger.warning("未找到新闻区域") return self def verify_contact_section(self) -> 'HomePage': """验证联系我们区域""" self.logger.section("验证联系我们区域") if self._is_visible("contact_section"): self.assert_element_visible("contact_title") self.assert_element_visible("contact_form") self.logger.info("联系区域包含表单") # 检查表单字段 form_fields = ["name", "email", "subject", "message"] for field in form_fields: if self._is_visible(f"contact_form #{field}"): self.logger.info(f"表单字段 {field} 存在") self.logger.info("✅ 联系区域验证通过") else: self.logger.warning("未找到联系区域") return self def verify_footer(self) -> 'HomePage': """验证页脚""" self.logger.section("验证页脚") self.assert_element_visible("footer") # 检查版权信息 footer_text = self._get_text("footer") if "睿新致远" in footer_text or "2026" in footer_text: self.logger.info("页脚包含版权信息") self.logger.info("✅ 页脚验证通过") return self def verify_all_sections(self) -> 'HomePage': """验证所有区域""" self.verify_header() self.verify_hero_section() self.verify_services_section() self.verify_products_section() self.verify_news_section() self.verify_contact_section() self.verify_footer() self.logger.info("✅ 首页所有区域验证完成") return self def scroll_to_section(self, section: str) -> 'HomePage': """滚动到指定区域""" self.logger.log_action(f"滚动到{section}区域") section_selectors = { "home": "#home, section:first-of-type", "about": "#about, section:has(h2:has-text('关于'))", "services": "#services, section:has(h2:has-text('业务')), section:has(h2:has-text('服务'))", "products": "#products, section:has(h2:has-text('产品'))", "news": "#news, section:has(h2:has-text('新闻')), section:has(h2:has-text('动态'))", "contact": "#contact, section:has(h2:has-text('联系')), section:has(h2:has-text('联系方式'))" } selector = section_selectors.get(section, f"#{section}") try: element = self.page.locator(selector).first if element.count() > 0: element.scroll_into_view_if_needed() self.logger.info(f"已滚动到{section}区域") else: self.logger.warning(f"未找到{section}区域") except Exception as e: self.logger.warning(f"滚动到{section}区域失败: {e}") return self def click_navigation_link(self, section: str) -> 'HomePage': """点击导航链接""" self.logger.log_action(f"点击{section}导航链接") nav_items = { "home": "首页", "about": "关于我们", "services": "核心业务", "products": "产品服务", "news": "新闻动态", "contact": "联系我们" } label = nav_items.get(section, section) # 查找包含指定文本的导航链接 nav_link = self.page.locator(f"nav a:has-text('{label}'), header a:has-text('{label}')") if nav_link.count() > 0: nav_link.first.click() self.wait_for_load() self.logger.info(f"已点击{nav_items.get(section, section)}链接") else: self.logger.warning(f"未找到{nav_items.get(section, section)}链接") return self def get_company_info(self) -> Dict[str, str]: """获取公司信息""" info = {} # 从首页获取描述 hero_text = "" if self._is_visible("hero_subtitle"): hero_text = self._get_text("hero_subtitle") # 如果无法从页面获取,使用默认值 info["description"] = hero_text if hero_text else "专注科技创新,驱动智慧未来" # 从常量获取 info["name"] = "四川睿新致远科技有限公司" info["slogan"] = "专注科技创新,驱动智慧未来" return info def get_statistics(self) -> Dict[str, int]: """获取统计数据""" stats = {} # 尝试从页面获取统计数据 if self._is_visible("about_section"): # 这里需要根据实际页面结构调整 pass # 默认值 stats = { "customers": 50, "cases": 100, "projects": 200, "experience": 8 } return stats def get_featured_services(self) -> List[Dict[str, str]]: """获取精选服务""" services = [] if self._is_visible("services_cards"): cards = self._find_all("services_cards")[:4] for card in cards: title = card.locator("h3, .title").text_content() if card.locator("h3, .title").count() > 0 else "" description = card.locator("p, .description").text_content() if card.locator("p, .description").count() > 0 else "" services.append({ "title": title.strip() if title else "", "description": description.strip() if description else "" }) return services def get_latest_news(self) -> List[Dict[str, str]]: """获取最新新闻""" news = [] if self._is_visible("news_items"): items = self._find_all("news_items")[:3] for item in items: title = item.locator("h3, .title, a").first.text_content() if item.locator("h3, .title, a").count() > 0 else "" date = item.locator(".date, time").first.text_content() if item.locator(".date, time").count() > 0 else "" news.append({ "title": title.strip() if title else "", "date": date.strip() if date else "" }) return news def verify_page_performance(self) -> Dict[str, float]: """验证页面性能指标""" self.logger.section("性能测试") performance_data = self.execute_js(""" () => { const timing = performance.timing; const navigation = performance.getEntriesByType('navigation')[0]; return { // 关键指标 'pageLoadTime': timing.loadEventEnd - timing.navigationStart, 'domContentLoaded': timing.domContentLoadedEventEnd - timing.navigationStart, 'firstPaint': timing.responseStart - timing.navigationStart, 'firstContentfulPaint': navigation ? navigation.firstContentfulPaint : 0, 'largestContentfulPaint': navigation ? navigation.largestContentfulPaint : 0, 'timeToInteractive': navigation ? navigation.interactive : 0, // 资源指标 'domainLookupTime': timing.domainLookupEnd - timing.domainLookupStart, 'serverResponseTime': timing.responseEnd - timing.requestStart, 'tcpConnectTime': timing.connectEnd - timing.connectStart, 'domInteractiveTime': timing.domInteractive - timing.domLoading }; } """) # 记录性能指标 for metric, value in performance_data.items(): if value and value > 0: threshold = get_settings().performance_thresholds.__dict__.get( metric.replace("_", ""), 3000 ) self.logger.log_performance(metric, float(value), threshold) return performance_data def verify_responsive_design(self, width: int, height: int) -> 'HomePage': """验证响应式设计""" self.logger.section(f"响应式测试 ({width}x{height})") # 设置视口大小 self.page.set_viewport_size({"width": width, "height": height}) self.wait_for_load() # 验证关键元素 self.assert_element_visible("header", timeout=5000) self.assert_element_visible("main", timeout=5000) self.assert_element_visible("footer", timeout=5000) # 根据屏幕大小调整验证逻辑 if width < 768: self.logger.info(f"移动端 {width}px: 验证基础布局") # 移动端检查汉堡菜单 mobile_menu = self.page.locator("button:has-text('菜单'), .mobile-menu, .menu-toggle") self.logger.info(f"发现 {mobile_menu.count()} 个移动端菜单元素") elif width < 1024: self.logger.info(f"平板端 {width}px: 验证平板布局") else: self.logger.info(f"桌面端 {width}px: 验证完整布局") self.logger.info(f"✅ {width}x{height} 响应式测试通过") return self