note title equip: DON/DOFF buttons outside top-left corner; User.active_title FK; don/doff views; greeting updated via JS — TDD
- lyric/models.py: active_title = ForeignKey('drama.Note', null=True, SET_NULL)
- lyric migration 0020_add_active_title
- billboard/views.py: don_title + doff_title views; is_equipped per note_item context
- billboard/urls.py: note/<slug>/don + note/<slug>/doff routes
- _navbar.html: id_greeting_name span; shows active_title.slug|capfirst when set
- my_notes.html: .note-don-doff buttons (DON/DOFF, × when disabled); data-don-url/doff-url/title attrs
- note-page.js: _bindDonDoff() — DON POST sets greeting + swaps btn state; DOFF restores Earthman
- _note.scss: .note-don-doff position:absolute left:-1rem top:0; flex-direction:column gap:1.25rem
- ITs: NoteEquipTitleViewTest (5 tests); UserModelTest.test_active_title_* (3 tests)
- FT: NoteEquipTitleTest.test_don_equips_title_greeting_and_doff_restores
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -282,6 +282,51 @@ class NoteSetPaletteViewTest(TestCase):
|
||||
self.assertEqual(self.user.palette, "palette-bardo")
|
||||
|
||||
|
||||
class NoteEquipTitleViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="don@test.io")
|
||||
self.client.force_login(self.user)
|
||||
self.note = Note.objects.create(
|
||||
user=self.user, slug="stargazer", earned_at=timezone.now(),
|
||||
)
|
||||
|
||||
def test_don_sets_active_title(self):
|
||||
self.client.post("/billboard/note/stargazer/don")
|
||||
self.user.refresh_from_db()
|
||||
self.assertEqual(self.user.active_title, self.note)
|
||||
|
||||
def test_doff_clears_active_title(self):
|
||||
self.user.active_title = self.note
|
||||
self.user.save(update_fields=["active_title"])
|
||||
self.client.post("/billboard/note/stargazer/doff")
|
||||
self.user.refresh_from_db()
|
||||
self.assertIsNone(self.user.active_title)
|
||||
|
||||
def test_don_returns_200_with_title(self):
|
||||
response = self.client.post("/billboard/note/stargazer/don")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json()["title"], "Stargazer")
|
||||
|
||||
def test_doff_returns_200(self):
|
||||
response = self.client.post("/billboard/note/stargazer/doff")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json(), {"ok": True})
|
||||
|
||||
def test_don_requires_login(self):
|
||||
self.client.logout()
|
||||
response = self.client.post("/billboard/note/stargazer/don")
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_don_returns_404_for_unowned_note(self):
|
||||
other = User.objects.create(email="other@test.io")
|
||||
Note.objects.create(user=other, slug="stargazer", earned_at=timezone.now())
|
||||
self.client.logout()
|
||||
self.client.force_login(other)
|
||||
response = self.client.post("/billboard/note/stargazer/don")
|
||||
# other user's own note — should work
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class SaveScrollPositionTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="test@savescroll.io")
|
||||
|
||||
Reference in New Issue
Block a user