buds rename + applet-list shell — Buddies → Buds everywhere (model field, slug, URL, view, DOM, CSS); my_buds.html + my_posts.html share new _applet-list-shell.html partial — vertical-title applet-scroll card; my_posts hosts two side-by-side in landscape, stacked in portrait — TDD
- lyric/0005 RemoveField+AddField (RenameField doesn't rename the implicit M2M through table; field was new in 0004 so no data loss). Lyric.User.buddies → User.buds; related_name added_as_buddy → added_as_bud.
- applets/0007 renames Applet slug my-buddies → my-buds + name 'My Buddies' → 'My Buds'. UI rationale: BILLBUDDIES overflowed the page-header band; in-game term collapses to BILLBUDS.
- billboard/0006 alter Line.Meta.ordering = ('created_at', 'id') — was already in models.py, just generates the corresponding migration (formalizing the ordering decision from the May-8b refactor).
- global rename via sed: buddies → buds, buddy → bud across 16 files (templates, SCSS, JS, ITs, FTs, page object, view code). 4 file renames via git mv: my_buddies.html → my_buds.html, _applet-my-buddies.html → _applet-my-buds.html, _buddy_panel.html → _bud_panel.html, _buddy_add_panel.html → _bud_add_panel.html, _buddy.scss → _bud.scss. Test files renamed too: test_buddies.py → test_buds.py, test_my_buddies.py → test_my_buds.py, test_buddy_btn.py → test_bud_btn.py. core.scss @import 'buddy' → 'bud'.
- new shared partial templates/apps/applets/_partials/_applet-list-shell.html — vertical-rotated <h2> + scrollable <ul> aperture, parameterised via {% include %} so a single page can invoke it more than once. Params: shell_title, shell_items, shell_item_template, shell_list_id, shell_empty.
- my_buds.html: single shell invocation w. add-bud panel below (page_class page-billbuds).
- my_posts.html: two shell invocations (own posts + posts shared with me) inside .applet-list-page--two-up — portrait stacks them; landscape lays side-by-side via @media (orientation: landscape) flex-direction: row (page_class page-billposts).
- SCSS: drop the bottom-anchored .buds-page block; new shared .applet-list-page (extends %billboard-page-base, flex-column + padding) w. .applet-scroll inside (extends %applet-box) and .applet-list inside that (flex: 1, overflow-y: auto). .applet-list-page--two-up flips to row layout in landscape. Body class trio gains page-billposts.
- 841 ITs + 5 my_buds/my_posts FTs green.
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
75
src/functional_tests/test_my_buds.py
Normal file
75
src/functional_tests/test_my_buds.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""FT for the My Buds page — bud btn + slide-out add flow.
|
||||
|
||||
Phase 1 of the buds sprint: explicit add via my_buds.html. Phase 2
|
||||
will layer autocomplete (sky-place-style top-3 username suggestions) and
|
||||
implicit auto-add on post-share / gate-invite.
|
||||
"""
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
from apps.lyric.models import User
|
||||
|
||||
from .base import FunctionalTest
|
||||
|
||||
|
||||
class MyBudsPageTest(FunctionalTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.gamer = User.objects.create(email="me@test.io", username="me")
|
||||
self.alice = User.objects.create(email="alice@test.io", username="alice")
|
||||
self.create_pre_authenticated_session("me@test.io")
|
||||
|
||||
def test_renders_existing_buds(self):
|
||||
"""Pre-existing buds show up as entries on first render."""
|
||||
self.gamer.buds.add(self.alice)
|
||||
self.browser.get(self.live_server_url + "/billboard/my-buds/")
|
||||
entry = self.wait_for(
|
||||
lambda: self.browser.find_element(By.CSS_SELECTOR, ".bud-entry .bud-name")
|
||||
)
|
||||
self.assertEqual(entry.text, "alice")
|
||||
|
||||
def test_empty_state_when_no_buds(self):
|
||||
self.browser.get(self.live_server_url + "/billboard/my-buds/")
|
||||
empty = self.wait_for(
|
||||
lambda: self.browser.find_element(By.CSS_SELECTOR, ".applet-list-entry--empty")
|
||||
)
|
||||
self.assertIn("No buds yet", empty.text)
|
||||
|
||||
def test_add_bud_via_bud_btn_appends_entry(self):
|
||||
self.browser.get(self.live_server_url + "/billboard/my-buds/")
|
||||
btn = self.wait_for(lambda: self.browser.find_element(By.ID, "id_bud_btn"))
|
||||
btn.click()
|
||||
|
||||
recipient = self.wait_for(lambda: self.browser.find_element(By.ID, "id_recipient"))
|
||||
recipient.send_keys("alice@test.io")
|
||||
ok = self.browser.find_element(By.CSS_SELECTOR, "#id_bud_panel .btn.btn-confirm")
|
||||
ok.click()
|
||||
|
||||
# New entry appears w. alice's username (not the bare email)
|
||||
self.wait_for(lambda: self.assertEqual(
|
||||
self.browser.find_element(
|
||||
By.CSS_SELECTOR, f".bud-entry[data-bud-id='{self.alice.id}'] .bud-name"
|
||||
).text,
|
||||
"alice",
|
||||
))
|
||||
# Server-side persisted
|
||||
self.wait_for(lambda: self.assertIn(
|
||||
self.alice, list(self.gamer.buds.all())
|
||||
))
|
||||
|
||||
def test_add_unregistered_email_is_silent_noop(self):
|
||||
self.browser.get(self.live_server_url + "/billboard/my-buds/")
|
||||
btn = self.wait_for(lambda: self.browser.find_element(By.ID, "id_bud_btn"))
|
||||
btn.click()
|
||||
recipient = self.wait_for(lambda: self.browser.find_element(By.ID, "id_recipient"))
|
||||
recipient.send_keys("ghost@test.io")
|
||||
ok = self.browser.find_element(By.CSS_SELECTOR, "#id_bud_panel .btn.btn-confirm")
|
||||
ok.click()
|
||||
|
||||
# Wait for the panel close (a positive signal the request landed)
|
||||
self.wait_for(lambda: self.assertNotIn(
|
||||
"active", btn.get_attribute("class")
|
||||
))
|
||||
# No bud entries (the empty-state row has its own --empty class)
|
||||
entries = self.browser.find_elements(By.CSS_SELECTOR, ".bud-entry")
|
||||
self.assertEqual(len(entries), 0)
|
||||
Reference in New Issue
Block a user