Phase 3: HTMX & Alpine.js integration, router refactoring, and UI modernization
CI / Test (Python 3.11) (push) Has been cancelled
CI / Test (Python 3.12) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Type Check (push) Has been cancelled
CI / Summary (push) Has been cancelled

- Modernized the frontend with HTMX for server-driven UI and Alpine.js for client state.
- Refactored anime, player, and recommendation logic into modular routers.
- Updated README.md to reflect the latest project state and technologies (v2.4).
- Added Plyr.io for an improved streaming experience.
- Improved project structure with componentized templates.
- Added Playwright and Vitest configuration for frontend testing.
This commit is contained in:
root
2026-03-26 10:34:26 +00:00
parent a684237725
commit 9f85908ff3
31 changed files with 3413 additions and 2201 deletions
-41
View File
@@ -1,41 +0,0 @@
import pytest
from playwright.sync_api import Page, expect
# Since we don't have a full running environment with auth easily mockable in pure Python Playwright
# without starting the server, I will write a test that can be run if the server is up.
# For CI/CD, we'd use a fixture to start the uvicorn server.
@pytest.mark.skip(reason="Requires running server and complex auth mock")
def test_tab_navigation(page: Page):
# Navigate to the app
page.goto("http://localhost:3000/web")
# Mock authentication state in localStorage and Alpine
page.evaluate("""() => {
localStorage.setItem('auth_token', 'mock-token');
document.body.__x.$data.isAuthenticated = true;
document.body.__x.$data.username = 'TestUser';
}""")
# Reload or wait for Alpine to react
page.reload()
# Verify Home tab is active by default
expect(page.locator("#tab-home")).to_be_visible()
expect(page.locator("button.tab:has-text('Accueil')")).to_have_class(/active/)
# Click on Anime tab
page.click("button.tab:has-text('Anime')")
# Verify Anime tab is shown and Home is hidden
expect(page.locator("#tab-anime")).to_be_visible()
expect(page.locator("#tab-home")).to_be_hidden()
expect(page.locator("button.tab:has-text('Anime')")).to_have_class(/active/)
# Click on Watchlist tab
page.click("button.tab:has-text('Watchlist')")
# Verify Watchlist tab is shown
expect(page.locator("#tab-watchlist")).to_be_visible()
expect(page.locator("#tab-anime")).to_be_hidden()
expect(page.locator("button.tab:has-text('Watchlist')")).to_have_class(/active/)
+40
View File
@@ -0,0 +1,40 @@
import pytest
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_anime_search_htmx():
"""Vérifie que la recherche d'anime renvoie du HTML avec HTMX"""
response = client.get("/api/anime/search?q=Naruto", headers={"HX-Request": "true"})
assert response.status_code == 200
assert "search-results-container" in response.text
assert "anime-card" in response.text
def test_series_search_htmx():
"""Vérifie que la recherche de séries renvoie du HTML avec HTMX"""
response = client.get("/api/series/search?q=Breaking", headers={"HX-Request": "true"})
assert response.status_code == 200
assert "search-results-container" in response.text
# On vérifie que soit on a des résultats, soit le message "aucune série trouvée"
assert "anime-grid" in response.text or "aucune série TV trouvée" in response.text.lower()
def test_recommendations_htmx():
"""Vérifie que les recommandations renvoient du HTML"""
response = client.get("/api/recommendations", headers={"HX-Request": "true"})
assert response.status_code == 200
assert "recommendations-grid" in response.text
def test_latest_releases_htmx():
"""Vérifie que les sorties récentes renvoient du HTML"""
response = client.get("/api/releases/latest", headers={"HX-Request": "true"})
assert response.status_code == 200
assert "releases-grid" in response.text
def test_episode_list_htmx():
"""Vérifie que la liste des épisodes renvoie du HTML"""
# Utilisation d'un lien bidon pour tester le rendu du composant
test_url = "https://anime-sama.fr/anime/vostfr/naruto"
response = client.get(f"/api/anime/episodes?url={test_url}", headers={"HX-Request": "true"})
assert response.status_code == 200
assert "episode-list-container" in response.text