feat(spa): add USE_SPA_UI cutover flag with legacy fallback
All checks were successful
CI / test-and-quality (push) Successful in 2m17s
CI / test-and-quality (pull_request) Successful in 2m5s

This commit is contained in:
2026-03-01 11:14:32 +00:00
parent 61eb08ad73
commit 1aa296c45c
6 changed files with 97 additions and 1 deletions

6
lobby/feature_flags.py Normal file
View File

@@ -0,0 +1,6 @@
from django.conf import settings
def use_spa_ui() -> bool:
"""Central read-point for SPA cutover flag."""
return bool(getattr(settings, "USE_SPA_UI", False))

View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="da">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WPP SPA</title>
</head>
<body>
<app-root data-wpp-shell-route="{{ shell_route }}"></app-root>
<script type="module" src="{{ spa_asset_base }}/main.js"></script>
</body>
</html>

View File

@@ -5,7 +5,7 @@ from pathlib import Path
from django.contrib.auth import get_user_model
from django.core.management import call_command
from django.test import TestCase
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils import timezone
@@ -973,6 +973,38 @@ class UiScreenTests(TestCase):
self.assertContains(response, "player_shell_runtime_error")
self.assertContains(response, "window.addEventListener(\"error\"")
@override_settings(USE_SPA_UI=False)
def test_legacy_templates_are_used_when_spa_flag_is_off(self):
self.client.login(username="host_ui", password="secret123")
host_response = self.client.get(reverse("lobby:host_screen"))
player_response = self.client.get(reverse("lobby:player_screen"))
self.assertContains(host_response, "Host panel")
self.assertContains(player_response, "Player panel")
self.assertNotContains(host_response, "<app-root")
self.assertNotContains(player_response, "<app-root")
@override_settings(USE_SPA_UI=True)
def test_host_screen_can_render_angular_shell_when_feature_flag_enabled(self):
self.client.login(username="host_ui", password="secret123")
response = self.client.get(reverse("lobby:host_screen"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "<app-root")
self.assertContains(response, "data-wpp-shell-route=\"/host\"")
self.assertContains(response, "/static/frontend/angular/browser/main.js")
@override_settings(USE_SPA_UI=True)
def test_player_screen_can_render_angular_shell_when_feature_flag_enabled(self):
response = self.client.get(reverse("lobby:player_screen"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "<app-root")
self.assertContains(response, "data-wpp-shell-route=\"/player\"")
self.assertContains(response, "/static/frontend/angular/browser/main.js")
class SessionDetailRoundQuestionTests(TestCase):
def setUp(self):

View File

@@ -1,14 +1,34 @@
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from fupogfakta.models import Category
from .feature_flags import use_spa_ui
def _render_spa_shell(request, shell_route: str):
return render(
request,
"lobby/spa_shell.html",
{
"shell_route": shell_route,
"spa_asset_base": settings.WPP_SPA_ASSET_BASE,
},
)
@login_required
def host_screen(request, spa_path=None):
if use_spa_ui():
return _render_spa_shell(request, "/host")
categories = Category.objects.filter(is_active=True).order_by("name")
return render(request, "lobby/host_screen.html", {"categories": categories})
def player_screen(request):
if use_spa_ui():
return _render_spa_shell(request, "/player")
return render(request, "lobby/player_screen.html")