Files
PlexPlaylistSync/tests/test_ui_regex_rules.py
T

367 lines
13 KiB
Python

"""
UI 集成测试 - 使用 Playwright 测试正则路径替换功能
安装:
pip install pytest-playwright
playwright install
运行:
pytest tests/test_ui_regex_rules.py --headed # 显示浏览器
pytest tests/test_ui_regex_rules.py # 无头模式
"""
import re
import time
from pathlib import Path
import pytest
from playwright.sync_api import Page, expect
# 测试服务器地址
BASE_URL = "http://localhost:8080"
@pytest.fixture(scope="session")
def test_server():
"""启动测试服务器(可选,如果服务器未运行)"""
# 如果你的服务器已经在运行,直接返回
# 否则可以在这里启动服务器进程
yield BASE_URL
@pytest.fixture
def page(page: Page, test_server):
"""配置页面并导航到首页"""
page.goto(test_server)
page.wait_for_load_state("networkidle")
return page
class TestRegexRulesUI:
"""测试正则路径替换规则的 UI 交互"""
def test_page_loads_successfully(self, page: Page):
"""测试页面成功加载"""
expect(page).to_have_title(re.compile("Plex.*Sync", re.I))
# 检查关键元素存在
expect(page.locator("h5:has-text('路径正则替换')")).to_be_visible()
expect(page.locator("#addRuleBtn")).to_be_visible()
def test_add_single_rule(self, page: Page):
"""测试添加单个规则"""
# 点击添加规则按钮
add_button = page.locator("#addRuleBtn")
# 获取初始规则数量
initial_count = page.locator(".rule-row").count()
# 添加一个规则
add_button.click()
page.wait_for_timeout(100) # 等待 DOM 更新
# 验证规则数量增加
new_count = page.locator(".rule-row").count()
assert new_count == initial_count + 1
# 填写规则内容
pattern_inputs = page.locator("input[name='pattern']")
replacement_inputs = page.locator("input[name='replacement']")
last_pattern = pattern_inputs.last
last_replacement = replacement_inputs.last
last_pattern.fill(r"/old/path/")
last_replacement.fill(r"/new/path/")
# 验证填写成功
assert last_pattern.input_value() == r"/old/path/"
assert last_replacement.input_value() == r"/new/path/"
def test_add_multiple_rules(self, page: Page):
"""测试添加多个规则"""
add_button = page.locator("#addRuleBtn")
rules = [
(r"\\\\nas\\Music", r"N:\\Music"),
(r"/music/cache/", r"/data/music/"),
(r"\\", r"/"),
]
# 添加多个规则
for pattern, replacement in rules:
add_button.click()
page.wait_for_timeout(100)
pattern_inputs = page.locator("input[name='pattern']")
replacement_inputs = page.locator("input[name='replacement']")
pattern_inputs.last.fill(pattern)
replacement_inputs.last.fill(replacement)
# 验证所有规则都已添加
pattern_inputs = page.locator("input[name='pattern']")
assert pattern_inputs.count() >= len(rules)
# 验证规则内容
for i, (pattern, replacement) in enumerate(rules):
# 注意:可能有初始规则,所以从后往前匹配
idx = pattern_inputs.count() - len(rules) + i
assert pattern in pattern_inputs.nth(idx).input_value()
def test_remove_rule(self, page: Page):
"""测试删除规则"""
add_button = page.locator("#addRuleBtn")
# 先添加一个规则
initial_count = page.locator(".rule-row").count()
add_button.click()
page.wait_for_timeout(100)
new_count = page.locator(".rule-row").count()
assert new_count == initial_count + 1
# 找到删除按钮(最后一个规则的删除按钮)
remove_buttons = page.locator(".rule-row button[title='删除此规则']")
remove_buttons.last.click()
page.wait_for_timeout(100)
# 验证规则已删除
final_count = page.locator(".rule-row").count()
assert final_count == initial_count
def test_save_rules(self, page: Page):
"""测试保存规则"""
# 清除现有规则
while page.locator(".rule-row").count() > 0:
remove_btn = page.locator(".rule-row button[title='删除此规则']").first
remove_btn.click()
page.wait_for_timeout(50)
# 添加测试规则
add_button = page.locator("#addRuleBtn")
add_button.click()
page.wait_for_timeout(100)
pattern_input = page.locator("input[name='pattern']").last
replacement_input = page.locator("input[name='replacement']").last
test_pattern = r"/test/path/"
test_replacement = r"/new/path/"
pattern_input.fill(test_pattern)
replacement_input.fill(test_replacement)
# 点击保存按钮
save_button = page.locator("button:has-text('保存规则')")
save_button.click()
# 等待页面响应
page.wait_for_load_state("networkidle")
# 验证成功消息(如果有)
# 可以检查是否有成功提示
success_indicator = page.locator(".alert-success, .text-success")
if success_indicator.count() > 0:
expect(success_indicator.first).to_be_visible()
def test_rules_persist_after_save(self, page: Page):
"""测试规则保存后持久化"""
# 清除并添加新规则
while page.locator(".rule-row").count() > 0:
page.locator(".rule-row button[title='删除此规则']").first.click()
page.wait_for_timeout(50)
add_button = page.locator("#addRuleBtn")
add_button.click()
page.wait_for_timeout(100)
test_pattern = r"C:\\Music"
test_replacement = r"D:\\Audio"
page.locator("input[name='pattern']").last.fill(test_pattern)
page.locator("input[name='replacement']").last.fill(test_replacement)
# 保存
page.locator("button:has-text('保存规则')").click()
page.wait_for_load_state("networkidle")
# 刷新页面
page.reload()
page.wait_for_load_state("networkidle")
# 验证规则仍然存在
pattern_inputs = page.locator("input[name='pattern']")
# 检查是否有匹配的规则
found = False
for i in range(pattern_inputs.count()):
if test_pattern in pattern_inputs.nth(i).input_value():
found = True
# 验证对应的替换值
replacement_value = page.locator("input[name='replacement']").nth(i).input_value()
assert test_replacement in replacement_value
break
assert found, f"未找到保存的规则: {test_pattern}"
def test_empty_pattern_validation(self, page: Page):
"""测试空模式验证"""
# 添加规则但不填写
add_button = page.locator("#addRuleBtn")
add_button.click()
page.wait_for_timeout(100)
# 只填写替换,不填写模式
replacement_input = page.locator("input[name='replacement']").last
replacement_input.fill("/new/path/")
# 尝试保存(应该有 HTML5 验证)
save_button = page.locator("button:has-text('保存规则')")
save_button.click()
# 验证是否有验证错误(pattern 有 required 属性)
pattern_input = page.locator("input[name='pattern']").last
# 检查 HTML5 验证
is_valid = pattern_input.evaluate("element => element.validity.valid")
assert not is_valid, "空模式应该触发验证错误"
def test_rule_order_preserved(self, page: Page):
"""测试规则顺序保持"""
# 清除现有规则
while page.locator(".rule-row").count() > 0:
page.locator(".rule-row button[title='删除此规则']").first.click()
page.wait_for_timeout(50)
# 按顺序添加多个规则
rules = [
("rule1", "replacement1"),
("rule2", "replacement2"),
("rule3", "replacement3"),
]
add_button = page.locator("#addRuleBtn")
for pattern, replacement in rules:
add_button.click()
page.wait_for_timeout(100)
page.locator("input[name='pattern']").last.fill(pattern)
page.locator("input[name='replacement']").last.fill(replacement)
# 验证顺序
pattern_inputs = page.locator("input[name='pattern']")
count = pattern_inputs.count()
for i, (pattern, _) in enumerate(rules):
idx = count - len(rules) + i
assert pattern in pattern_inputs.nth(idx).input_value()
class TestComplexScenarios:
"""测试复杂场景"""
def test_windows_to_linux_path_conversion(self, page: Page):
"""测试 Windows 到 Linux 路径转换场景"""
# 清除规则
while page.locator(".rule-row").count() > 0:
page.locator(".rule-row button[title='删除此规则']").first.click()
page.wait_for_timeout(50)
# 添加转换规则
conversion_rules = [
(r"C:\\Music", r"/mnt/music"),
(r"\\", r"/"),
]
add_button = page.locator("#addRuleBtn")
for pattern, replacement in conversion_rules:
add_button.click()
page.wait_for_timeout(100)
page.locator("input[name='pattern']").last.fill(pattern)
page.locator("input[name='replacement']").last.fill(replacement)
# 保存
page.locator("button:has-text('保存规则')").click()
page.wait_for_load_state("networkidle")
# 验证保存成功
success_indicator = page.locator(".alert-success, .text-success, :has-text('保存')")
if success_indicator.count() > 0:
expect(success_indicator.first).to_be_visible(timeout=5000)
def test_nas_path_normalization(self, page: Page):
"""测试 NAS 路径规范化"""
# 清除规则
while page.locator(".rule-row").count() > 0:
page.locator(".rule-row button[title='删除此规则']").first.click()
page.wait_for_timeout(50)
# NAS 路径规范化规则
nas_rules = [
(r"\\\\koha9-nas\\koha9-nas\\Music", r"N:\\Music"),
(r"/music/cache/", r"/data/music/"),
(r"\\", r"/"),
]
add_button = page.locator("#addRuleBtn")
for pattern, replacement in nas_rules:
add_button.click()
page.wait_for_timeout(100)
page.locator("input[name='pattern']").last.fill(pattern)
page.locator("input[name='replacement']").last.fill(replacement)
# 保存并验证
page.locator("button:has-text('保存规则')").click()
page.wait_for_load_state("networkidle")
# 刷新验证持久化
page.reload()
page.wait_for_load_state("networkidle")
# 验证所有规则都保存了
pattern_inputs = page.locator("input[name='pattern']")
saved_patterns = [pattern_inputs.nth(i).input_value() for i in range(pattern_inputs.count())]
for pattern, _ in nas_rules:
assert any(pattern in saved for saved in saved_patterns), f"规则未保存: {pattern}"
@pytest.mark.slow
class TestPerformance:
"""性能测试"""
def test_add_many_rules_performance(self, page: Page):
"""测试添加大量规则的性能"""
# 清除规则
while page.locator(".rule-row").count() > 0:
page.locator(".rule-row button[title='删除此规则']").first.click()
page.wait_for_timeout(50)
# 测试添加 20 个规则
add_button = page.locator("#addRuleBtn")
start_time = time.time()
for i in range(20):
add_button.click()
page.wait_for_timeout(50)
page.locator("input[name='pattern']").last.fill(f"pattern{i}")
page.locator("input[name='replacement']").last.fill(f"replacement{i}")
end_time = time.time()
# 验证时间合理(应该在几秒内完成)
elapsed = end_time - start_time
assert elapsed < 10, f"添加 20 个规则耗时过长: {elapsed:.2f}s"
# 验证数量
assert page.locator(".rule-row").count() >= 20
if __name__ == "__main__":
pytest.main([__file__, "-v", "--headed"])