Add personal notes, DeepSearch history, backup offload, and gateway auth throttling
DevPlace CI / test (push) Failing after 28m20s

Also streamline the top navigation: drop the Tools dropdown, make Quizzes
and Battles icon-only entries, and remove the Workspace, Containers and
Editor entry points from the project detail page.
This commit is contained in:
2026-09-12 20:32:31 +02:00
parent c4f7d01b2d
commit cae139ead9
158 changed files with 6156 additions and 377 deletions
+117
View File
@@ -406,3 +406,120 @@ def test_live_incoming_message_shows_report_button(alice, bob):
bubble = page_b.locator(f".message-bubble.theirs:has-text('{msg}')")
bubble.wait_for(state="visible", timeout=10000)
expect(bubble.locator(".message-report-btn")).to_be_visible()
def test_send_button_locks_during_send_and_prevents_duplicate(alice, bob):
page_a, user_a = alice
page_b, user_b = bob
uid_b = get_table("users").find_one(username=user_b["username"])["uid"]
page_a.goto(
f"{BASE_URL}/messages?with_uid={uid_b}", wait_until="domcontentloaded"
)
msg = f"Locked send {int(time.time() * 1000)}"
textarea = page_a.locator(".messages-input-area textarea[name='content']")
textarea.wait_for(state="visible")
textarea.fill(msg)
send_btn = page_a.locator(".messages-send-btn")
send_btn.click()
# the composer refuses a duplicate submit while this send is unconfirmed
expect(send_btn).to_be_disabled()
bubble = page_a.locator(f".message-bubble.mine:has-text('{msg}')")
reconciled = page_a.locator(
f".message-bubble.mine:has-text('{msg}')[data-msg-uid]:not(.pending)"
)
reconciled.first.wait_for(state="visible", timeout=10000)
expect(send_btn).to_be_enabled()
assert bubble.count() == 1
def test_scroll_up_is_not_yanked_by_incoming_message(alice, bob):
from datetime import datetime, timedelta, timezone
from devplacepy.utils import generate_uid
page_a, user_a = alice
page_b, user_b = bob
uid_a = get_table("users").find_one(username=user_a["username"])["uid"]
uid_b = get_table("users").find_one(username=user_b["username"])["uid"]
base = datetime.now(timezone.utc) - timedelta(minutes=5)
for i in range(40):
get_table("messages").insert(
{
"uid": generate_uid(),
"sender_uid": uid_a if i % 2 == 0 else uid_b,
"receiver_uid": uid_b if i % 2 == 0 else uid_a,
"content": f"scroll history line {i}",
"read": True,
"created_at": (base + timedelta(seconds=i)).isoformat(),
"updated_at": None,
}
)
page_a.goto(
f"{BASE_URL}/messages?with_uid={uid_b}", wait_until="domcontentloaded"
)
page_b.goto(
f"{BASE_URL}/messages?with_uid={uid_a}", wait_until="domcontentloaded"
)
thread = page_a.locator(".messages-thread")
thread.wait_for(state="visible")
thread.locator(".message-bubble").first.wait_for(state="visible")
thread.evaluate(
"el => { el.scrollTop = 0; el.dispatchEvent(new Event('scroll')); }"
)
page_a.wait_for_timeout(200)
scroll_before = thread.evaluate("el => el.scrollTop")
assert scroll_before < 50
msg = f"While scrolled up {int(time.time() * 1000)}"
textarea_b = page_b.locator(".messages-input-area textarea[name='content']")
textarea_b.wait_for(state="visible")
textarea_b.fill(msg)
page_b.locator(".messages-send-btn").click()
page_a.locator(f".message-bubble.theirs:has-text('{msg}')").first.wait_for(
state="visible", timeout=10000
)
scroll_after = thread.evaluate("el => el.scrollTop")
assert scroll_after < 50, (
"an incoming message must not yank a scrolled-up reader back to the bottom"
)
def test_scroll_stays_pinned_to_bottom_for_active_reader(alice, bob):
page_a, user_a = alice
page_b, user_b = bob
uid_a = get_table("users").find_one(username=user_a["username"])["uid"]
uid_b = get_table("users").find_one(username=user_b["username"])["uid"]
page_a.goto(
f"{BASE_URL}/messages?with_uid={uid_b}", wait_until="domcontentloaded"
)
page_b.goto(
f"{BASE_URL}/messages?with_uid={uid_a}", wait_until="domcontentloaded"
)
thread = page_a.locator(".messages-thread")
thread.wait_for(state="visible")
msg = f"Pinned bottom {int(time.time() * 1000)}"
textarea_b = page_b.locator(".messages-input-area textarea[name='content']")
textarea_b.wait_for(state="visible")
textarea_b.fill(msg)
page_b.locator(".messages-send-btn").click()
page_a.locator(f".message-bubble.theirs:has-text('{msg}')").first.wait_for(
state="visible", timeout=10000
)
at_bottom = thread.evaluate(
"el => el.scrollHeight - el.scrollTop - el.clientHeight < 100"
)
assert at_bottom, "an active-at-bottom reader must still auto-scroll to new messages"