"""FT — post.html gear menu (NVM / DEL / BYE).
User-authored Posts grow a #id_gear_btn that opens #id_post_menu with:
- NVM (everyone) → navigates to billboard:my_posts
- DEL (owner) → hard-deletes the Post
- BYE (invitee) → removes the viewer from post.shared_with
Admin-Posts (kind=NOTE_UNLOCK) get the gear menu too but with NVM only —
DEL + BYE don't apply to system-authored threads.
Mirrors apps/gameboard/_partials/_room_gear.html behaviour; see
test_game_room_gatekeeper.py {owner_can_delete_room, gamer_can_abandon_room}
for the room-side analogues.
"""
from selenium.webdriver.common.by import By
from apps.billboard.models import Line, Post
from apps.drama.models import Note
from apps.lyric.models import User
from .base import FunctionalTest
class OwnerPostGearTest(FunctionalTest):
"""Owner of a user-Post sees NVM + DEL in the gear menu; DEL nukes
the Post and redirects to /billboard/users//."""
def setUp(self):
super().setUp()
self.owner = User.objects.create(email="owner@test.io")
self.post = Post.objects.create(
owner=self.owner, kind=Post.KIND_USER_POST, title="Doomed post",
)
Line.objects.create(post=self.post, text="line 1", author=self.owner)
self.create_pre_authenticated_session("owner@test.io")
def test_owner_can_delete_post_via_gear_menu(self):
self.browser.get(
self.live_server_url + f"/billboard/post/{self.post.id}/"
)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gear-btn")
).click()
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, "#id_post_menu .btn-danger"
)
).click()
self.confirm_guard()
self.wait_for(lambda: self.assertIn(
f"/billboard/users/{self.owner.id}/", self.browser.current_url
))
self.assertFalse(Post.objects.filter(id=self.post.id).exists())
def test_gear_menu_for_owner_shows_NVM_and_DEL_but_not_BYE(self):
self.browser.get(
self.live_server_url + f"/billboard/post/{self.post.id}/"
)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gear-btn")
).click()
menu = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_post_menu")
)
self.assertTrue(menu.find_elements(By.CSS_SELECTOR, ".btn-cancel"))
self.assertTrue(menu.find_elements(By.CSS_SELECTOR, ".btn-danger"))
self.assertFalse(menu.find_elements(By.CSS_SELECTOR, ".btn-abandon"))
class InviteePostGearTest(FunctionalTest):
"""Invitee on a user-Post sees NVM + BYE; BYE removes the viewer
from post.shared_with (owner + other invitees keep the Post)."""
def setUp(self):
super().setUp()
self.owner = User.objects.create(email="owner@test.io")
self.invitee = User.objects.create(email="invitee@test.io")
self.other = User.objects.create(email="other@test.io")
self.post = Post.objects.create(
owner=self.owner, kind=Post.KIND_USER_POST, title="Shared post",
)
Line.objects.create(post=self.post, text="line 1", author=self.owner)
self.post.shared_with.add(self.invitee, self.other)
self.create_pre_authenticated_session("invitee@test.io")
def test_invitee_can_abandon_post_via_gear_menu(self):
self.browser.get(
self.live_server_url + f"/billboard/post/{self.post.id}/"
)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gear-btn")
).click()
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, "#id_post_menu .btn-abandon"
)
).click()
self.confirm_guard()
self.wait_for(lambda: self.assertIn(
f"/billboard/users/{self.invitee.id}/", self.browser.current_url
))
self.post.refresh_from_db()
self.assertNotIn(self.invitee, self.post.shared_with.all())
# Post survives — owner + other invitee unaffected
self.assertEqual(self.post.owner, self.owner)
self.assertIn(self.other, self.post.shared_with.all())
def test_gear_menu_for_invitee_shows_NVM_and_BYE_but_not_DEL(self):
self.browser.get(
self.live_server_url + f"/billboard/post/{self.post.id}/"
)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gear-btn")
).click()
menu = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_post_menu")
)
self.assertTrue(menu.find_elements(By.CSS_SELECTOR, ".btn-cancel"))
self.assertTrue(menu.find_elements(By.CSS_SELECTOR, ".btn-abandon"))
self.assertFalse(menu.find_elements(By.CSS_SELECTOR, ".btn-danger"))
class AdminPostGearTest(FunctionalTest):
"""Admin-Post (kind=NOTE_UNLOCK) shows the gear menu with NVM only —
DEL + BYE don't apply to system-authored threads."""
def setUp(self):
super().setUp()
self.gamer = User.objects.create(email="gamer@test.io")
Note.grant_if_new(self.gamer, "stargazer")
self.admin_post = Post.objects.get(
owner=self.gamer, kind=Post.KIND_NOTE_UNLOCK,
)
self.create_pre_authenticated_session("gamer@test.io")
def test_admin_post_gear_menu_has_NVM_but_no_DEL_no_BYE(self):
self.browser.get(
self.live_server_url + f"/billboard/post/{self.admin_post.id}/"
)
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gear-btn")
).click()
menu = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_post_menu")
)
self.assertTrue(menu.find_elements(By.CSS_SELECTOR, ".btn-cancel"))
self.assertFalse(menu.find_elements(By.CSS_SELECTOR, ".btn-danger"))
self.assertFalse(menu.find_elements(By.CSS_SELECTOR, ".btn-abandon"))