Bug fixes: - Fix menu button not appearing: Use app.registerExtension() for proper ComfyUI initialization timing. The button now appears in the top bar next to the Manager button. - Fix source_hint incorrectly saved as source_url: Pattern-matched hints like "Lightricks/LTX-Video" are not valid URLs and should not be used for update checks. - Fix deprecated tempfile.mktemp(): Use secure mkstemp() instead to avoid race conditions. - Add os import for file descriptor handling in update_scheduler.py Test improvements: - Update Playwright tests to hover over UI to reveal ComfyUI menu - All 26 tests now passing (17 unit + 9 UI) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
163 lines
4.8 KiB
Python
163 lines
4.8 KiB
Python
"""Playwright UI tests for ComfyUI-ModelSync."""
|
|
|
|
import pytest
|
|
from playwright.sync_api import Page, expect
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def comfyui_url():
|
|
"""ComfyUI URL - uses gandalf server."""
|
|
return "http://gandalf.local:8188"
|
|
|
|
|
|
def test_comfyui_loads(page: Page, comfyui_url: str):
|
|
"""Test that ComfyUI loads successfully."""
|
|
page.goto(comfyui_url)
|
|
page.wait_for_load_state("networkidle")
|
|
|
|
# ComfyUI should have loaded
|
|
expect(page).to_have_title("ComfyUI")
|
|
|
|
|
|
def test_modelsync_button_exists(page: Page, comfyui_url: str):
|
|
"""Test that ModelSync button appears in menu."""
|
|
page.goto(comfyui_url)
|
|
page.wait_for_load_state("networkidle")
|
|
|
|
# Wait for ComfyUI to fully initialize and hover to reveal UI
|
|
page.wait_for_timeout(5000)
|
|
page.mouse.move(10, 10)
|
|
page.wait_for_timeout(2000)
|
|
|
|
# Look for Model Sync button
|
|
modelsync_btn = page.locator('button:has-text("Model Sync")')
|
|
expect(modelsync_btn).to_be_visible(timeout=10000)
|
|
|
|
|
|
def test_modelsync_panel_opens(page: Page, comfyui_url: str):
|
|
"""Test that clicking ModelSync button opens the panel."""
|
|
page.goto(comfyui_url)
|
|
page.wait_for_load_state("networkidle")
|
|
page.wait_for_timeout(5000)
|
|
page.mouse.move(10, 10)
|
|
page.wait_for_timeout(2000)
|
|
|
|
# Click Model Sync button
|
|
page.locator('button:has-text("Model Sync")').click()
|
|
|
|
# Panel should appear
|
|
panel = page.locator(".modelsync-panel")
|
|
expect(panel).to_be_visible(timeout=5000)
|
|
|
|
# Header should show
|
|
header = page.locator(".modelsync-header h2")
|
|
expect(header).to_have_text("Model Sync")
|
|
|
|
|
|
def test_modelsync_tabs_work(page: Page, comfyui_url: str):
|
|
"""Test that panel tabs are clickable."""
|
|
page.goto(comfyui_url)
|
|
page.wait_for_load_state("networkidle")
|
|
page.wait_for_timeout(5000)
|
|
page.mouse.move(10, 10)
|
|
page.wait_for_timeout(2000)
|
|
|
|
# Open panel
|
|
page.locator("button:has-text('Model Sync')").click()
|
|
page.wait_for_selector(".modelsync-panel")
|
|
|
|
# Click Sync Targets tab
|
|
page.locator(".modelsync-tab:has-text('Sync Targets')").click()
|
|
targets_content = page.locator("#modelsync-targets")
|
|
expect(targets_content).not_to_have_class("hidden")
|
|
|
|
# Click Settings tab
|
|
page.locator(".modelsync-tab:has-text('Settings')").click()
|
|
settings_content = page.locator("#modelsync-settings")
|
|
expect(settings_content).not_to_have_class("hidden")
|
|
|
|
# Click back to Models tab
|
|
page.locator(".modelsync-tab:has-text('Models')").click()
|
|
models_content = page.locator("#modelsync-models")
|
|
expect(models_content).not_to_have_class("hidden")
|
|
|
|
|
|
def test_scan_models_button(page: Page, comfyui_url: str):
|
|
"""Test that Scan button triggers model scan."""
|
|
page.goto(comfyui_url)
|
|
page.wait_for_load_state("networkidle")
|
|
page.wait_for_timeout(5000)
|
|
page.mouse.move(10, 10)
|
|
page.wait_for_timeout(2000)
|
|
|
|
# Open panel
|
|
page.locator("button:has-text('Model Sync')").click()
|
|
page.wait_for_selector(".modelsync-panel")
|
|
|
|
# Click Scan button
|
|
scan_btn = page.locator("button[data-action='scan']")
|
|
scan_btn.click()
|
|
|
|
# Status should update
|
|
status = page.locator(".modelsync-status")
|
|
page.wait_for_timeout(2000)
|
|
|
|
# Status should show scanning result
|
|
status_text = status.text_content()
|
|
assert "Scanned" in status_text or "models" in status_text.lower()
|
|
|
|
|
|
def test_panel_closes(page: Page, comfyui_url: str):
|
|
"""Test that panel can be closed."""
|
|
page.goto(comfyui_url)
|
|
page.wait_for_load_state("networkidle")
|
|
page.wait_for_timeout(5000)
|
|
page.mouse.move(10, 10)
|
|
page.wait_for_timeout(2000)
|
|
|
|
# Open panel
|
|
page.locator("button:has-text('Model Sync')").click()
|
|
page.wait_for_selector(".modelsync-panel")
|
|
|
|
# Close with X button
|
|
page.locator(".modelsync-close").click()
|
|
|
|
# Panel should be gone
|
|
panel = page.locator(".modelsync-panel")
|
|
expect(panel).not_to_be_visible()
|
|
|
|
|
|
def test_api_status_endpoint(page: Page, comfyui_url: str):
|
|
"""Test the API status endpoint directly."""
|
|
response = page.request.get(f"{comfyui_url}/modelsync/status")
|
|
assert response.ok
|
|
|
|
data = response.json()
|
|
assert "scheduler" in data
|
|
assert "models" in data
|
|
assert "sync" in data
|
|
|
|
|
|
def test_api_models_endpoint(page: Page, comfyui_url: str):
|
|
"""Test the API models endpoint."""
|
|
response = page.request.get(f"{comfyui_url}/modelsync/models")
|
|
assert response.ok
|
|
|
|
data = response.json()
|
|
assert "models" in data
|
|
assert isinstance(data["models"], list)
|
|
|
|
|
|
def test_api_config_endpoint(page: Page, comfyui_url: str):
|
|
"""Test the API config endpoint."""
|
|
response = page.request.get(f"{comfyui_url}/modelsync/config")
|
|
assert response.ok
|
|
|
|
data = response.json()
|
|
assert "auto_update_enabled" in data
|
|
assert "check_interval_hours" in data
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v", "--headed"])
|