""" 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"])