场景
由于我测试的应用存在设计态和运行态两个,所以我需要对这两个态都进行验证。但是我的运行态又要依赖于设计态的应用才能进行页面跳转。
问题
我一直卡在这个问题这,以为是我的元素定位不对,但两个态的登录组件都是一样,元素的id及xpath也都是一样的。甚至怀疑是不是内存泄漏。最终都没解决。
控制台一直提示错误
Stacktrace:
GetHandleVerifier [0x00007FF6F4A76CB5+28821]
(No symbol) [0x00007FF6F49E3840]
(No symbol) [0x00007FF6F488578A]
(No symbol) [0x00007FF6F48D91BE]
(No symbol) [0x00007FF6F48D94AC]
(No symbol) [0x00007FF6F4922647]
(No symbol) [0x00007FF6F48FF33F]
(No symbol) [0x00007FF6F491F412]
(No symbol) [0x00007FF6F48FF0A3]
(No symbol) [0x00007FF6F48CA778]
(No symbol) [0x00007FF6F48CB8E1]
GetHandleVerifier [0x00007FF6F4DAFCAD+3408013]
GetHandleVerifier [0x00007FF6F4DC741F+3504127]
GetHandleVerifier [0x00007FF6F4DBB5FD+3455453]
GetHandleVerifier [0x00007FF6F4B3BDBB+835995]
(No symbol) [0x00007FF6F49EEB5F]
(No symbol) [0x00007FF6F49EA814]
(No symbol) [0x00007FF6F49EA9AD]
(No symbol) [0x00007FF6F49DA199]
BaseThreadInitThunk [0x00007FFC9BBA7374+20]
RtlUserThreadStart [0x00007FFC9D17CC91+33]
问题分析
对于以上场景及问题分析,以及一系列网上资料搜索,还有反复询问ai,最终归结为"页面跳转到新窗口并操作新页面"
问题解决
runtime_login_page.py
from pages.base_page import PageObject, PageElement, MultiPageElement
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import logging
# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class RunTimeLoginPage(PageObject):
# 运行态账户输入框 username
run_account = (By.ID, "username")
# 运行态密码输入框 password
run_password=(By.ID, "password")
# 登录按钮
login_btn = PageElement(xpath="//span[contains(.,'登 录')]")
# 访问系统按钮
visit_btn=(By.XPATH, "//span[contains(.,'访问系统')]")
# 重置密码弹框确定按钮
sure_btn=PageElement(xpath="//span[contains(.,'确 定')]")
def __init__(self, driver):
super().__init__(driver)
self.driver=driver
self.vars = {}
def wait_for_window(self, timeout=2000):
"""等待新窗口出现并返回其句柄"""
original_handles = set(self.driver.window_handles)
new_handle = None
try:
# 使用显式等待直到窗口句柄数量发生变化
WebDriverWait(self.driver, timeout / 1000).until(
lambda d: len(d.window_handles) != len(original_handles)
)
# 获取所有窗口句柄,并找出新增的那个
current_handles = set(self.driver.window_handles)
new_handle = list(current_handles.difference(original_handles))[0]
print(f"New window handle detected: {new_handle}")
# 保存新的窗口句柄到 self.vars
self.vars["win9871"] = new_handle
except Exception as e:
print(f"Timeout waiting for new window: {e}")
return new_handle
def navigate_to_runtime_page(self):
"""点击触发打开新窗口的操作"""
try:
element=WebDriverWait(self.driver, 20).until(
EC.visibility_of_element_located(self.visit_btn)
)
element.click()
# 打印当前窗口句柄数量以确认是否有新窗口打开
print(f"Number of window handles after clicking: {len(self.driver.window_handles)}")
except Exception as e:
logger.error(f"An error occurred during runtime login: {e}", exc_info=True)
def switch_to_new_window(self, timeout=2000):
"""切换到新开的窗口"""
new_window_handle = self.wait_for_window(timeout)
if new_window_handle:
self.driver.switch_to.window(new_window_handle)
# 打印当前窗口标题以确认切换成功
print(f"Switched to new window with title: {self.driver.title}")
else:
raise Exception("New window did not open within the specified timeout.")
def login_runtime(self, username, password):
logger.info('enter')
try: # 确保页面加载完成
WebDriverWait(self.driver, 20).until(
lambda driver: driver.execute_script('return document.readyState') == 'complete'
)
# 等待账户输入框可见
account_input = WebDriverWait(self.driver, 20).until(
EC.visibility_of_element_located(self.run_account)
)
logger.info("Account input is visible")
account_input.send_keys(username)
WebDriverWait(self.driver, 20).until(
lambda driver: driver.execute_script('return document.readyState') == 'complete'
)
# 等待密码输入框可见
password_input = WebDriverWait(self.driver, 20).until(
EC.visibility_of_element_located(self.run_password)
)
logger.info("Password input is visible")
password_input.send_keys(password)
self.login_btn.click()
except Exception as e:
logger.error(f"An error occurred during runtime login: {e}", exc_info=True)
def locate_sure_btn(self):
self.sure_btn.click()
解析:
wait_for_window: 检测新窗口函数
使用显式等待和集合运算来确保新窗口句柄被正确捕获,并立即将其存储在 self.vars 中以便后续引用。
navigate_to_runtime_page:确认新窗口确实打开
确保点击按钮或链接确实触发了新窗口或标签页的打开
switch_to_new_window:处理新窗口/标签页切换
确保在找到新窗口句柄后立即切换到该窗口,并验证页面加载完成
test_login.py
from pages.runtime_login_page import RunTimeLoginPage
from pages.app_page import AppPage
from utils.data_provider import load_test_data
from pages.pub_page import PubPage
import pytest
import time
from .test_app import search_app
test_data = load_test_data('data/test_data.json')
# 登录运行态
@pytest.mark.usefixtures("login")
def test_login_runtime(login):
runtime_login_page = RunTimeLoginPage(login)
app_page=AppPage(login)
# 执行搜索应用操作前打印当前窗口信息
# print(f"Before search_app: Current window handle is {login.current_window_handle}, title is {login.title}")
search_app(login,test_data['create_app']['appname'])
# 执行搜索应用操作后打印当前窗口信息
print(f"After search_app: Current window handle is {login.current_window_handle}, title is {login.title}")
# 导航到运行时页面并切换窗口
runtime_login_page.navigate_to_runtime_page()
# 切换到新窗口前打印当前窗口信息
# print(
# f"Before switching to new window: Current window handle is {login.current_window_handle}, title is {login.title}")
runtime_login_page.switch_to_new_window()
# 切换到新窗口后打印当前窗口信息
# print(
# f"After switching to new window: Current window handle is {login.current_window_handle}, title is {login.title}")
runtime_login_page.login_runtime(test_data['runtime_valid_user']['username'],test_data['runtime_valid_user']['originPassword'])
# 登录后再次确认当前窗口信息
# print(f"After login_runtime: Current window handle is {login.current_window_handle}, title is {login.title}")
app_page.locate_modify_password(test_data['runtime_valid_user']['password'],test_data['runtime_valid_user']['password'])
runtime_login_page.locate_sure_btn()
time.sleep(20)