1. 新增 Joyhub_ui_auto_test/ 目录: - tests/ - 测试用例目录 - pages/ - 页面元素定位 - config/ - 配置文件 - utils/ - 工具类 - test_data/ - 测试数据 - reports/ - 测试报告 - webapp-testing/ - WebApp测试相关 2. 配置文件: - pytest.ini - pytest配置 - requirements.txt - 依赖列表 - README.md - 项目说明
156 lines
6.4 KiB
Python
156 lines
6.4 KiB
Python
from pathlib import Path
|
||
import random
|
||
import re
|
||
import sys
|
||
|
||
import pytest
|
||
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
|
||
from playwright.sync_api import expect
|
||
|
||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||
if str(PROJECT_ROOT) not in sys.path:
|
||
sys.path.insert(0, str(PROJECT_ROOT))
|
||
|
||
from config.settings import settings
|
||
from tests.test_rewards_shopping_cart_login_logout import _accept_cookie_if_present
|
||
from utils.logger import get_logger
|
||
|
||
logger = get_logger(__name__)
|
||
|
||
|
||
REDEEM_NOW_SELECTORS = [
|
||
'button:has-text("Redeem Now"):not([disabled])',
|
||
'button:has-text("Redeem now"):not([disabled])',
|
||
'button:has-text("立即兑换"):not([disabled])',
|
||
]
|
||
POINTS_PAY_SELECTORS = [
|
||
'button:has-text("Pay"):not([disabled])',
|
||
'button:has-text("Confirm"):not([disabled])',
|
||
'button:has-text("Submit"):not([disabled])',
|
||
'button:has-text("Place Order"):not([disabled])',
|
||
'button:has-text("Redeem"):not([disabled])',
|
||
'button:has-text("Confirm Redemption"):not([disabled])',
|
||
'button:has-text("确认"):not([disabled])',
|
||
'button:has-text("提交"):not([disabled])',
|
||
'button:has-text("支付"):not([disabled])',
|
||
]
|
||
|
||
|
||
def _visible_locator(page, selectors, timeout=5_000):
|
||
for selector in selectors:
|
||
locator = page.locator(selector).first
|
||
try:
|
||
if locator.is_visible(timeout=timeout):
|
||
return locator
|
||
except PlaywrightTimeoutError:
|
||
continue
|
||
raise AssertionError(f"未找到可见元素: {selectors}")
|
||
|
||
|
||
def _login_in_open_dialog(page):
|
||
logger.info("在 Redeem Now 触发的登录弹窗中登录")
|
||
login_inputs = page.locator('input[type="text"]')
|
||
expect(login_inputs.first).to_be_visible(timeout=settings.default_timeout)
|
||
login_inputs.nth(0).fill(settings.login_email)
|
||
login_inputs.nth(1).fill(settings.verification_code)
|
||
|
||
confirmation_items = page.locator("div.inline-flex.cursor-pointer")
|
||
expect(confirmation_items.nth(0)).to_be_visible(timeout=settings.default_timeout)
|
||
expect(confirmation_items.nth(1)).to_be_visible(timeout=settings.default_timeout)
|
||
confirmation_items.nth(0).click()
|
||
confirmation_items.nth(1).click()
|
||
|
||
submit_button = page.locator("button").last
|
||
expect(submit_button).to_be_enabled(timeout=settings.default_timeout)
|
||
submit_button.click()
|
||
|
||
logger.info("等待登录弹窗关闭或兑换流程继续")
|
||
expect(login_inputs.first).not_to_be_visible(timeout=settings.default_timeout)
|
||
|
||
|
||
def _open_points_redemption_from_rewards(page):
|
||
logger.info("未登录用户点击 Rewards 并进入 Points Redemption")
|
||
rewards_link = page.locator('header a[href^="javascript:"]').filter(has_text="Rewards").first
|
||
expect(rewards_link).to_be_attached(timeout=settings.default_timeout)
|
||
rewards_link.click(force=True)
|
||
|
||
try:
|
||
page.wait_for_function(
|
||
"""
|
||
() => Array.from(document.querySelectorAll('a[href="/points-redemption"]'))
|
||
.some(element => !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length))
|
||
""",
|
||
timeout=5_000,
|
||
)
|
||
page.locator('a[href="/points-redemption"]').filter(has_text="Points Redemption").first.click()
|
||
except PlaywrightTimeoutError:
|
||
logger.info("Rewards 下拉入口未稳定展示,直接进入 Points Redemption 页面")
|
||
page.goto(settings.base_url.rstrip("/") + "/points-redemption", wait_until="domcontentloaded")
|
||
|
||
expect(page).to_have_url(re.compile(r".*/points-redemption/?"), timeout=settings.default_timeout)
|
||
expect(page.locator("body")).to_be_visible(timeout=settings.default_timeout)
|
||
|
||
|
||
def _open_random_redeemable_points_product(page):
|
||
logger.info("随机选择一个积分商品并查找 Redeem Now")
|
||
category_buttons = page.locator("section button").filter(has_not_text=re.compile(r"Accepet|Accept", re.I))
|
||
expect(category_buttons.first).to_be_visible(timeout=settings.default_timeout)
|
||
|
||
category_indices = list(range(category_buttons.count()))
|
||
random.shuffle(category_indices)
|
||
last_error = None
|
||
|
||
for category_index in category_indices:
|
||
try:
|
||
logger.info("尝试积分商品分类/商品入口索引: %s", category_index)
|
||
category_buttons.nth(category_index).click()
|
||
page.wait_for_timeout(1_500)
|
||
|
||
if page.get_by_text(re.compile(r"currently do not support delivery", re.I)).first.is_visible(timeout=1_000):
|
||
last_error = "当前国家/地区不支持积分商品配送"
|
||
continue
|
||
|
||
redeem_button = _visible_locator(page, REDEEM_NOW_SELECTORS, timeout=5_000)
|
||
expect(redeem_button).to_be_enabled(timeout=settings.default_timeout)
|
||
redeem_button.click()
|
||
return
|
||
except (AssertionError, PlaywrightTimeoutError) as exc:
|
||
last_error = exc
|
||
logger.info("当前积分商品不可兑换,继续尝试下一个: %s", exc)
|
||
|
||
pytest.skip(f"当前测试环境未展示可兑换积分商品,无法执行 Redeem Now 主流程: {last_error}")
|
||
|
||
|
||
def _complete_points_payment_after_login(page):
|
||
logger.info("登录后继续完成积分商品支付/兑换")
|
||
try:
|
||
pay_button = _visible_locator(page, POINTS_PAY_SELECTORS, timeout=settings.default_timeout)
|
||
expect(pay_button).to_be_enabled(timeout=settings.default_timeout)
|
||
pay_button.click()
|
||
except AssertionError:
|
||
logger.info("登录后未出现独立确认按钮,检查是否已自动提交兑换")
|
||
|
||
result_text = page.get_by_text(
|
||
re.compile(r"success|successful|completed|paid|payment|order|redeem|redemption|成功|订单|兑换", re.I)
|
||
).first
|
||
expect(result_text).to_be_visible(timeout=settings.default_timeout)
|
||
|
||
|
||
def test_guest_points_product_redeem_now_login_then_points_payment_result(page, ensure_guest_user):
|
||
logger.info("未登录用户从 Rewards 选择积分商品,Redeem Now 后登录并完成积分支付")
|
||
page.goto(settings.base_url, wait_until="domcontentloaded")
|
||
ensure_guest_user()
|
||
|
||
_open_points_redemption_from_rewards(page)
|
||
_open_random_redeemable_points_product(page)
|
||
|
||
logger.info("校验 Redeem Now 后弹出登录弹窗")
|
||
expect(page.locator('input[type="text"]').first).to_be_visible(timeout=settings.default_timeout)
|
||
|
||
_login_in_open_dialog(page)
|
||
_complete_points_payment_after_login(page)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(pytest.main([str(Path(__file__))]))
|