forked from retoor/devplacepy
feat: add per-user customization suppression toggles and API endpoints for profile
This commit is contained in:
+2
-8
@@ -36,19 +36,13 @@ os.environ["DEVPLACE_SITEMAP_TTL"] = "0"
|
||||
|
||||
|
||||
def run_async(coro):
|
||||
"""Run a coroutine from synchronous test code, regardless of event loop state."""
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
return asyncio.run(coro)
|
||||
|
||||
"""Run a coroutine from sync test code on a dedicated thread and fresh loop."""
|
||||
result_box = []
|
||||
exc_box = []
|
||||
|
||||
def _run():
|
||||
try:
|
||||
future = asyncio.run_coroutine_threadsafe(coro, loop)
|
||||
result_box.append(future.result())
|
||||
result_box.append(asyncio.run(coro))
|
||||
except BaseException as e:
|
||||
exc_box.append(e)
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import time
|
||||
|
||||
import requests
|
||||
from playwright.sync_api import expect
|
||||
|
||||
from tests.conftest import BASE_URL
|
||||
from devplacepy.database import (
|
||||
CUSTOMIZATION_GLOBAL_SCOPE,
|
||||
get_customization_prefs,
|
||||
get_table,
|
||||
list_custom_overrides,
|
||||
set_custom_override,
|
||||
set_customization_pref,
|
||||
)
|
||||
from devplacepy.utils import clear_user_cache
|
||||
|
||||
|
||||
def _user(username):
|
||||
return get_table("users").find_one(username=username)
|
||||
|
||||
|
||||
def _seed_global_css(uid, sentinel):
|
||||
set_custom_override(
|
||||
"user", uid, CUSTOMIZATION_GLOBAL_SCOPE, "css", f".{sentinel} {{ color: red; }}"
|
||||
)
|
||||
|
||||
|
||||
def _profile(page, username):
|
||||
page.goto(f"{BASE_URL}/profile/{username}", wait_until="domcontentloaded")
|
||||
|
||||
|
||||
def _click_toggle(page, category):
|
||||
page.locator(f"[data-customization-toggle='{category}']").click()
|
||||
page.locator(".dialog-overlay.visible .dialog-confirm").click()
|
||||
|
||||
|
||||
def test_owner_sees_toggles_and_initial_state(bob):
|
||||
page, user = bob
|
||||
uid = _user(user["username"])["uid"]
|
||||
set_customization_pref(uid, "global", disabled=False)
|
||||
set_customization_pref(uid, "pagetype", disabled=False)
|
||||
_seed_global_css(uid, "owner-sees-toggle")
|
||||
|
||||
_profile(page, user["username"])
|
||||
expect(page.locator("[data-customization]")).to_be_visible()
|
||||
expect(page.locator("[data-customization-toggle='global']")).to_have_text("Disable")
|
||||
expect(page.locator("[data-customization-toggle='pagetype']")).to_have_text(
|
||||
"Disable"
|
||||
)
|
||||
|
||||
|
||||
def test_global_toggle_suppresses_rendering_and_persists(bob):
|
||||
page, user = bob
|
||||
uid = _user(user["username"])["uid"]
|
||||
set_customization_pref(uid, "global", disabled=False)
|
||||
sentinel = f"sentinel-{int(time.time() * 1000)}"
|
||||
_seed_global_css(uid, sentinel)
|
||||
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
assert sentinel in page.content()
|
||||
|
||||
_profile(page, user["username"])
|
||||
_click_toggle(page, "global")
|
||||
expect(page.locator("[data-customization-toggle='global']")).to_have_text("Enable")
|
||||
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
assert sentinel not in page.content()
|
||||
|
||||
_profile(page, user["username"])
|
||||
expect(page.locator("[data-customization-toggle='global']")).to_have_text("Enable")
|
||||
expect(
|
||||
page.locator("form[action$='/customization/global'] input[name='value']")
|
||||
).to_have_value("1")
|
||||
|
||||
_click_toggle(page, "global")
|
||||
expect(page.locator("[data-customization-toggle='global']")).to_have_text("Disable")
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
assert sentinel in page.content()
|
||||
|
||||
|
||||
def test_pagetype_toggle_is_independent(bob):
|
||||
page, user = bob
|
||||
uid = _user(user["username"])["uid"]
|
||||
set_customization_pref(uid, "global", disabled=False)
|
||||
set_customization_pref(uid, "pagetype", disabled=False)
|
||||
global_sentinel = f"glob-{int(time.time() * 1000)}"
|
||||
page_sentinel = f"page-{int(time.time() * 1000)}"
|
||||
_seed_global_css(uid, global_sentinel)
|
||||
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
page_type = page.locator("main[data-page-type]").get_attribute("data-page-type")
|
||||
set_custom_override(
|
||||
"user", uid, page_type, "css", f".{page_sentinel} {{ color: blue; }}"
|
||||
)
|
||||
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
body = page.content()
|
||||
assert global_sentinel in body
|
||||
assert page_sentinel in body
|
||||
|
||||
_profile(page, user["username"])
|
||||
_click_toggle(page, "pagetype")
|
||||
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
body = page.content()
|
||||
assert global_sentinel in body
|
||||
assert page_sentinel not in body
|
||||
|
||||
|
||||
def test_disable_keeps_rows(bob):
|
||||
page, user = bob
|
||||
uid = _user(user["username"])["uid"]
|
||||
set_customization_pref(uid, "global", disabled=False)
|
||||
_seed_global_css(uid, "keep-rows")
|
||||
|
||||
_profile(page, user["username"])
|
||||
_click_toggle(page, "global")
|
||||
expect(page.locator("[data-customization-toggle='global']")).to_have_text("Enable")
|
||||
|
||||
rows = [
|
||||
r
|
||||
for r in list_custom_overrides("user", uid)
|
||||
if r["scope"] == CUSTOMIZATION_GLOBAL_SCOPE
|
||||
]
|
||||
assert rows, "disabling must not delete the stored override row"
|
||||
|
||||
|
||||
def test_admin_operates_on_other_user(alice, seeded_db):
|
||||
admin_page, admin_user = alice
|
||||
target = _user(seeded_db["bob"]["username"])
|
||||
admin_uid = _user(admin_user["username"])["uid"]
|
||||
get_table("users").update({"uid": admin_uid, "role": "Admin"}, ["uid"])
|
||||
clear_user_cache(admin_uid)
|
||||
set_customization_pref(target["uid"], "global", disabled=False)
|
||||
|
||||
_profile(admin_page, target["username"])
|
||||
expect(admin_page.locator("[data-customization]")).to_be_visible()
|
||||
_click_toggle(admin_page, "global")
|
||||
expect(
|
||||
admin_page.locator("[data-customization-toggle='global']")
|
||||
).to_have_text("Enable")
|
||||
|
||||
assert get_customization_prefs("user", target["uid"])["disable_global"] is True
|
||||
|
||||
|
||||
def test_non_owner_non_admin_hidden_and_guarded(bob):
|
||||
page, user = bob
|
||||
member = _user(user["username"])
|
||||
get_table("users").update({"uid": member["uid"], "role": "Member"}, ["uid"])
|
||||
clear_user_cache(member["uid"])
|
||||
target = _user("alice_test")
|
||||
|
||||
_profile(page, target["username"])
|
||||
assert page.locator("[data-customization]").count() == 0
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/profile/{target['username']}/customization/global",
|
||||
data={"value": "1"},
|
||||
headers={"Accept": "application/json", "X-API-KEY": member["api_key"]},
|
||||
allow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 403
|
||||
Reference in New Issue
Block a user