367 lines
13 KiB
Python
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"])
|