fix: 适配 hash 路由登录页 (/#/login)
This commit is contained in:
@@ -116,7 +116,9 @@ def ensure_login(web_config, page, config):
|
|||||||
if auth_role == role:
|
if auth_role == role:
|
||||||
# 验证登录态是否仍然有效
|
# 验证登录态是否仍然有效
|
||||||
page.goto(base_url)
|
page.goto(base_url)
|
||||||
if "/login" not in page.url:
|
# Hash 路由模式下判断是否重定向到登录页
|
||||||
|
page.wait_for_timeout(2000) # 等 hash 路由完成
|
||||||
|
if "/#/login" not in page.url and "/login" not in page.url:
|
||||||
return # 登录态有效
|
return # 登录态有效
|
||||||
# 登录态过期,给出明确指引
|
# 登录态过期,给出明确指引
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
@@ -137,22 +139,19 @@ def ensure_login(web_config, page, config):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# ── skip_login: false,执行 UI 登录(可能有验证码,会失败) ──
|
# ── skip_login: false,执行 UI 登录(可能有验证码,会失败) ──
|
||||||
page.goto(f"{base_url}/login")
|
page.goto(f"{base_url}/#/login")
|
||||||
page.get_by_placeholder("请输入用户名").fill(account["phone"])
|
page.get_by_placeholder("请输入用户名").fill(account["phone"])
|
||||||
page.get_by_placeholder("请输入密码").fill(account["password"])
|
page.get_by_placeholder("请输入密码").fill(account["password"])
|
||||||
page.get_by_role("button", name="登录").click()
|
page.get_by_role("button", name="登录").click()
|
||||||
|
|
||||||
# 如果有图形验证码,这里会超时
|
# 等几秒让页面处理路由跳转
|
||||||
try:
|
page.wait_for_timeout(3000)
|
||||||
page.wait_for_url(f"{base_url}/**", timeout=10_000)
|
if "/#/login" in page.url or "/login" in page.url:
|
||||||
except Exception:
|
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"登录失败!可能存在图形验证码。\n"
|
"登录失败!可能存在图形验证码。\n"
|
||||||
"请改为手动登录生成 auth 文件: python tests/web/save_auth.py --role {role}"
|
"请改为手动登录生成 auth 文件: python tests/web/save_auth.py --role {role}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return _ensure_login
|
|
||||||
|
|
||||||
|
|
||||||
def _load_auth_state(context_kwargs: dict, storage_dir: Path, browser: str) -> str | None:
|
def _load_auth_state(context_kwargs: dict, storage_dir: Path, browser: str) -> str | None:
|
||||||
"""尝试加载登录态,返回角色名或 None。"""
|
"""尝试加载登录态,返回角色名或 None。"""
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class NavBar(BasePage):
|
|||||||
def logout(self) -> None:
|
def logout(self) -> None:
|
||||||
"""退出登录。"""
|
"""退出登录。"""
|
||||||
self.click_when_ready(self.logout_btn)
|
self.click_when_ready(self.logout_btn)
|
||||||
expect(self.page).to_have_url("**/login**", timeout=10_000)
|
expect(self.page).to_have_url("#/login", timeout=10_000)
|
||||||
|
|
||||||
def is_expanded(self) -> bool:
|
def is_expanded(self) -> bool:
|
||||||
"""检查侧边栏是否展开。"""
|
"""检查侧边栏是否展开。"""
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ class LoginPage(BasePage):
|
|||||||
self.click_when_ready(self.login_btn)
|
self.click_when_ready(self.login_btn)
|
||||||
|
|
||||||
if expect_success:
|
if expect_success:
|
||||||
# 等待登录成功后页面跳转
|
# 等待登录成功后页面跳转(hash 路由模式)
|
||||||
self.page.wait_for_url("**/admin/**", timeout=10_000)
|
self.page.wait_for_timeout(3000)
|
||||||
|
assert "/#/login" not in self.page.url and "/login" not in self.page.url, \
|
||||||
|
"登录失败,仍在登录页"
|
||||||
else:
|
else:
|
||||||
# 预期登录失败,停留在当前页
|
# 预期登录失败,停留在当前页
|
||||||
expect(self.page).to_have_url("**/login**", timeout=5_000)
|
expect(self.page).to_have_url("**/login**", timeout=5_000)
|
||||||
@@ -48,5 +50,5 @@ class LoginPage(BasePage):
|
|||||||
return self.error_tip.inner_text()
|
return self.error_tip.inner_text()
|
||||||
|
|
||||||
def is_on_login_page(self) -> bool:
|
def is_on_login_page(self) -> bool:
|
||||||
"""检查是否仍在登录页。"""
|
"""检查是否仍在登录页(兼容 hash 路由)。"""
|
||||||
return "/login" in self.page.url
|
return "/login" in self.page.url or "/#/login" in self.page.url
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
base_url = web_config["base_url"]
|
base_url = web_config["base_url"]
|
||||||
login_url = f"{base_url}/login"
|
login_url = f"{base_url}/#/login"
|
||||||
|
|
||||||
# ── 打开浏览器 ──
|
# ── 打开浏览器 ──
|
||||||
from playwright.sync_api import sync_playwright
|
from playwright.sync_api import sync_playwright
|
||||||
@@ -98,7 +98,8 @@ def main():
|
|||||||
|
|
||||||
# ── 等待用户手动完成验证码 + 登录 ──
|
# ── 等待用户手动完成验证码 + 登录 ──
|
||||||
try:
|
try:
|
||||||
page.wait_for_url(f"{base_url}/**", timeout=300_000) # 5 分钟超时
|
# Hash 路由模式,用元素可见判断登录成功而不是 URL 变化
|
||||||
|
page.wait_for_selector(".ant-layout-sider, .sidebar, [class*='sider']", timeout=300_000)
|
||||||
except Exception:
|
except Exception:
|
||||||
print("\n⚠️ 等待超时(5 分钟)。可能原因:")
|
print("\n⚠️ 等待超时(5 分钟)。可能原因:")
|
||||||
print(" 1. 未在浏览器中完成验证码登录")
|
print(" 1. 未在浏览器中完成验证码登录")
|
||||||
@@ -109,7 +110,7 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# ── 验证登录成功 ──
|
# ── 验证登录成功 ──
|
||||||
if "/login" in page.url:
|
if "/login" in page.url or "/#/login" in page.url:
|
||||||
print("\n❌ 登录失败,仍然停留在登录页。请检查账号密码。")
|
print("\n❌ 登录失败,仍然停留在登录页。请检查账号密码。")
|
||||||
browser.close()
|
browser.close()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ class TestLoginSmoke:
|
|||||||
"""验证加载预登录态后可直接访问管理后台,无需重新登录。"""
|
"""验证加载预登录态后可直接访问管理后台,无需重新登录。"""
|
||||||
ensure_login("super_admin")
|
ensure_login("super_admin")
|
||||||
page.goto(web_config["base_url"])
|
page.goto(web_config["base_url"])
|
||||||
|
# 等 hash 路由完成
|
||||||
|
page.wait_for_timeout(2000)
|
||||||
# 已登录状态下不应重定向到登录页
|
# 已登录状态下不应重定向到登录页
|
||||||
assert "/login" not in page.url, (
|
assert "#/login" not in page.url and "/login" not in page.url, (
|
||||||
f"预登录态失效!仍然重定向到登录页。请运行: python tests/web/save_auth.py"
|
f"预登录态失效!仍然重定向到登录页。请运行: python tests/web/save_auth.py"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,7 +42,8 @@ class TestLoginSmoke:
|
|||||||
"""验证车队长预登录态有效。"""
|
"""验证车队长预登录态有效。"""
|
||||||
ensure_login("team_leader")
|
ensure_login("team_leader")
|
||||||
page.goto(web_config["base_url"])
|
page.goto(web_config["base_url"])
|
||||||
assert "/login" not in page.url
|
page.wait_for_timeout(2000)
|
||||||
|
assert "#/login" not in page.url and "/login" not in page.url
|
||||||
|
|
||||||
@pytest.mark.p0
|
@pytest.mark.p0
|
||||||
@pytest.mark.smoke
|
@pytest.mark.smoke
|
||||||
@@ -62,29 +65,31 @@ class TestLoginPageDirect:
|
|||||||
@pytest.mark.p1
|
@pytest.mark.p1
|
||||||
def test_login_page_accessible(self, page: Page, web_config):
|
def test_login_page_accessible(self, page: Page, web_config):
|
||||||
"""验证登录页可直接访问并正常渲染。"""
|
"""验证登录页可直接访问并正常渲染。"""
|
||||||
page.goto(f"{web_config['base_url']}/login")
|
page.goto(f"{web_config['base_url']}/#/login")
|
||||||
# 登录页应存在用户名输入框
|
# 登录页应存在用户名输入框
|
||||||
assert page.get_by_placeholder("请输入用户名").is_visible()
|
assert page.get_by_placeholder("请输入用户名").is_visible()
|
||||||
|
|
||||||
@pytest.mark.p1
|
@pytest.mark.p1
|
||||||
def test_login_page_elements(self, page: Page, web_config):
|
def test_login_page_elements(self, page: Page, web_config):
|
||||||
"""验证登录页所有关键元素存在。"""
|
"""验证登录页所有关键元素存在。"""
|
||||||
page.goto(f"{web_config['base_url']}/login")
|
page.goto(f"{web_config['base_url']}/#/login")
|
||||||
assert page.get_by_placeholder("请输入用户名").is_visible()
|
assert page.get_by_placeholder("请输入用户名").is_visible()
|
||||||
assert page.get_by_placeholder("请输入密码").is_visible()
|
assert page.get_by_placeholder("请输入密码").is_visible()
|
||||||
assert page.get_by_role("button", name="登录").is_visible()
|
assert page.get_by_role("button", name="登录").is_visible()
|
||||||
|
|
||||||
@pytest.mark.p1
|
@pytest.mark.p1
|
||||||
def test_double_login_session_redirect(self, page: Page, web_config, ensure_login):
|
def test_double_login_session_redirect(self, page: Page, web_config, ensure_login):
|
||||||
"""验证已登录用户访问 /login 页自动重定向。"""
|
"""验证已登录用户访问登录页自动重定向。"""
|
||||||
ensure_login("super_admin")
|
ensure_login("super_admin")
|
||||||
page.goto(web_config["base_url"])
|
page.goto(web_config["base_url"])
|
||||||
assert "/login" not in page.url
|
page.wait_for_timeout(2000)
|
||||||
|
assert "#/login" not in page.url and "/login" not in page.url
|
||||||
|
|
||||||
# 已登录状态再次访问 /login
|
# 已登录状态再次访问登录页
|
||||||
page.goto(f"{web_config['base_url']}/login")
|
page.goto(f"{web_config['base_url']}/#/login")
|
||||||
|
page.wait_for_timeout(2000)
|
||||||
# 应被重定向到后台
|
# 应被重定向到后台
|
||||||
assert "/login" not in page.url, (
|
assert "#/login" not in page.url and "/login" not in page.url, (
|
||||||
f"已登录用户访问 /login 应被重定向,当前: {page.url}"
|
f"已登录用户访问 /login 应被重定向,当前: {page.url}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,4 +107,5 @@ class TestLoginUI:
|
|||||||
"""通过 UI 完成完整登录(需要手动关闭 skip_login)。"""
|
"""通过 UI 完成完整登录(需要手动关闭 skip_login)。"""
|
||||||
ensure_login("super_admin")
|
ensure_login("super_admin")
|
||||||
page.goto(web_config["base_url"])
|
page.goto(web_config["base_url"])
|
||||||
assert "/login" not in page.url
|
page.wait_for_timeout(2000)
|
||||||
|
assert "#/login" not in page.url and "/login" not in page.url
|
||||||
|
|||||||
Reference in New Issue
Block a user