recognition: page, palette modal, & dashboard palette unlock — TDD
- billboard/recognition/ view + template; recognition/<slug>/set-palette endpoint (no trailing slash) - recognition.html: <template>-based modal (clone on open, remove on close — Selenium find_elements compatible) - recognition-page.js: image-box → modal → swatch preview → body-click restore → OK → confirm → POST set-palette - _palettes_for_user() replaces static PALETTES; Recognition.palette unlocks swatch + populates data-shoptalk - _unlocked_palettes_for_user() wires dynamic unlock check into set_palette view - _applet-palette.html: data-shoptalk from context instead of hard-coded "Placeholder" - _recognition.scss: banner, recog-list/item, image-box, modal, palette-confirm; :not([hidden]) pattern avoids display override - FT T2 split into T2a (banner → FYI → recog page), T2b (palette modal flow), T2c (dashboard palette applet) - 684 ITs green; 7 FTs green Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import json as _json
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.applets.models import Applet
|
||||
from apps.drama.models import GameEvent, ScrollPosition, record
|
||||
from apps.drama.models import GameEvent, Recognition, ScrollPosition, record
|
||||
from apps.epic.models import Room
|
||||
from apps.lyric.models import User
|
||||
|
||||
@@ -161,6 +164,102 @@ class BillscrollViewTest(TestCase):
|
||||
self.assertContains(response, 'class="drama-event-time"')
|
||||
|
||||
|
||||
class RecognitionPageViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="recog@test.io")
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def test_requires_login(self):
|
||||
self.client.logout()
|
||||
response = self.client.get("/billboard/recognition/")
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_returns_200(self):
|
||||
response = self.client.get("/billboard/recognition/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_uses_recognition_template(self):
|
||||
response = self.client.get("/billboard/recognition/")
|
||||
self.assertTemplateUsed(response, "apps/billboard/recognition.html")
|
||||
|
||||
def test_passes_recognitions_in_context(self):
|
||||
recog = Recognition.objects.create(
|
||||
user=self.user, slug="stargazer", earned_at=timezone.now()
|
||||
)
|
||||
response = self.client.get("/billboard/recognition/")
|
||||
self.assertIn(recog, response.context["recognitions"])
|
||||
|
||||
def test_excludes_other_users_recognitions(self):
|
||||
other = User.objects.create(email="other@test.io")
|
||||
Recognition.objects.create(
|
||||
user=other, slug="stargazer", earned_at=timezone.now()
|
||||
)
|
||||
response = self.client.get("/billboard/recognition/")
|
||||
self.assertEqual(list(response.context["recognitions"]), [])
|
||||
|
||||
def test_renders_recog_list_and_items(self):
|
||||
Recognition.objects.create(
|
||||
user=self.user, slug="stargazer", earned_at=timezone.now()
|
||||
)
|
||||
response = self.client.get("/billboard/recognition/")
|
||||
self.assertContains(response, 'class="recog-list"')
|
||||
self.assertContains(response, 'class="recog-item"')
|
||||
|
||||
def test_renders_recog_item_title_description_image_box(self):
|
||||
Recognition.objects.create(
|
||||
user=self.user, slug="stargazer", earned_at=timezone.now()
|
||||
)
|
||||
response = self.client.get("/billboard/recognition/")
|
||||
self.assertContains(response, 'class="recog-item__title"')
|
||||
self.assertContains(response, 'class="recog-item__description"')
|
||||
self.assertContains(response, 'class="recog-item__image-box"')
|
||||
|
||||
|
||||
class RecognitionSetPaletteViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="setpal@test.io")
|
||||
self.client.force_login(self.user)
|
||||
self.recognition = Recognition.objects.create(
|
||||
user=self.user, slug="stargazer", earned_at=timezone.now(),
|
||||
)
|
||||
self.url = "/billboard/recognition/stargazer/set-palette"
|
||||
|
||||
def test_requires_login(self):
|
||||
self.client.logout()
|
||||
response = self.client.post(
|
||||
self.url,
|
||||
data=_json.dumps({"palette": "palette-bardo"}),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_sets_palette_on_recognition(self):
|
||||
self.client.post(
|
||||
self.url,
|
||||
data=_json.dumps({"palette": "palette-bardo"}),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.recognition.refresh_from_db()
|
||||
self.assertEqual(self.recognition.palette, "palette-bardo")
|
||||
|
||||
def test_returns_200_with_ok(self):
|
||||
response = self.client.post(
|
||||
self.url,
|
||||
data=_json.dumps({"palette": "palette-bardo"}),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json(), {"ok": True})
|
||||
|
||||
def test_returns_404_for_slug_user_does_not_own(self):
|
||||
response = self.client.post(
|
||||
"/billboard/recognition/schizo/set-palette",
|
||||
data=_json.dumps({"palette": "palette-bardo"}),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
|
||||
class SaveScrollPositionTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="test@savescroll.io")
|
||||
|
||||
Reference in New Issue
Block a user