Update
DevPlace CI / test (push) Has been cancelled

This commit is contained in:
2026-06-19 10:06:09 +02:00
parent 823fff0e6a
commit 016d2d1c76
136 changed files with 3025 additions and 564 deletions
+17 -6
View File
@@ -4,7 +4,12 @@ import time
import requests
from tests.conftest import BASE_URL, login_user
from devplacepy import config
from devplacepy.database import get_table, get_primary_admin_uid, refresh_snapshot
from devplacepy.database import (
get_table,
get_primary_admin_uid,
invalidate_admins_cache,
refresh_snapshot,
)
from devplacepy.services.backup import store
from devplacepy.utils import clear_user_cache
@@ -29,6 +34,7 @@ def _make_admin():
row = get_table("users").find_one(username=name)
get_table("users").update({"uid": row["uid"], "role": "Admin"}, ["uid"])
clear_user_cache(row["uid"])
invalidate_admins_cache()
return {"email": f"{name}@t.dev", "password": password, "uid": row["uid"]}
@@ -84,15 +90,20 @@ def test_download_enabled_for_primary_admin(page, app_server):
for uid in others:
get_table("users").update({"uid": uid, "role": "Member"}, ["uid"])
clear_user_cache(uid)
invalidate_admins_cache()
try:
assert get_primary_admin_uid() == admin["uid"]
backup_uid = _seed_done_backup(admin["uid"])
login_user(page, admin)
page.goto(f"{BASE_URL}/admin/backups", wait_until="domcontentloaded")
row = _row(page, backup_uid)
row.wait_for(state="visible", timeout=15000)
link = row.locator("a.admin-btn:has-text('Download')")
link = None
for _ in range(20):
page.goto(f"{BASE_URL}/admin/backups", wait_until="domcontentloaded")
row = _row(page, backup_uid)
row.wait_for(state="visible", timeout=15000)
link = row.locator("a.admin-btn:has-text('Download')")
if link.count() > 0:
break
time.sleep(0.3)
link.wait_for(state="visible")
assert link.get_attribute("href") == f"/admin/backups/{backup_uid}/download"
assert row.locator("button.admin-btn[title='Not available']").count() == 0
+2 -4
View File
@@ -503,10 +503,9 @@ def test_feed_tab_switching(alice):
def test_feed_topic_filter_sidebar(alice):
page, user = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".sidebar-more .sidebar-more-toggle").click()
topics = ["Devlog", "Showcase", "Question", "Rant", "Fun"]
for topic in topics:
link = page.locator(f"a:has-text('{topic}')").first
link = page.locator(f".sidebar-nav a:has-text('{topic}')").first
assert link.is_visible()
@@ -689,8 +688,7 @@ def test_feed_guest_reply_disabled(page, app_server):
def test_feed_signals_topic(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".sidebar-more .sidebar-more-toggle").click()
signals_link = page.locator("a:has-text('Signals')")
signals_link = page.locator(".sidebar-nav a:has-text('Signals')")
assert signals_link.is_visible()
+45
View File
@@ -1,6 +1,8 @@
# retoor <retoor@molodetz.nl>
import re
import time
from playwright.sync_api import expect
from tests.conftest import BASE_URL
from devplacepy.database import get_table
import requests
@@ -211,6 +213,49 @@ def test_messages_search_for_bob(alice):
assert result.is_visible()
def test_messages_search_first_result_highlighted(alice):
page, _ = alice
page.goto(f"{BASE_URL}/messages", wait_until="domcontentloaded")
page.fill("#message-search", "bob_test")
result = page.locator(".search-dropdown-item:has-text('bob_test')")
result.wait_for(state="visible", timeout=5000)
expect(result.first).to_have_class(re.compile(r"\bactive\b"))
def test_messages_search_enter_opens_conversation(alice):
page, _ = alice
page.goto(f"{BASE_URL}/messages", wait_until="domcontentloaded")
page.fill("#message-search", "bob_test")
page.locator(".search-dropdown-item:has-text('bob_test')").wait_for(
state="visible", timeout=5000
)
page.locator("#message-search").press("Enter")
page.wait_for_url(re.compile(r"with_uid="), wait_until="domcontentloaded")
def test_messages_search_arrow_then_enter_opens_conversation(alice):
page, _ = alice
page.goto(f"{BASE_URL}/messages", wait_until="domcontentloaded")
page.fill("#message-search", "bob_test")
page.locator(".search-dropdown-item:has-text('bob_test')").wait_for(
state="visible", timeout=5000
)
search = page.locator("#message-search")
search.press("ArrowDown")
search.press("Enter")
page.wait_for_url(re.compile(r"with_uid="), wait_until="domcontentloaded")
def test_messages_search_escape_closes_dropdown(alice):
page, _ = alice
page.goto(f"{BASE_URL}/messages", wait_until="domcontentloaded")
page.fill("#message-search", "bob_test")
result = page.locator(".search-dropdown-item:has-text('bob_test')")
result.wait_for(state="visible", timeout=5000)
page.locator("#message-search").press("Escape")
expect(result).to_be_hidden()
def test_messages_header_visible(alice):
page, _ = alice
page.goto(f"{BASE_URL}/messages", wait_until="domcontentloaded")
+1
View File
@@ -195,6 +195,7 @@ def _seed_news_image(news_uid):
"deleted_by": None,
"news_uid": news_uid,
"url": "https://example.com/seo-news-image.jpg",
"is_placeholder": 0,
}
)
def _seed_feed_posts(count):
+54 -1
View File
@@ -124,11 +124,64 @@ def test_comment_voted_state_persists(alice):
)
def test_mention_first_match_highlighted(alice):
page, _ = alice
create_post(page, "random", "Mention highlight test post")
textarea = page.locator(".comment-form textarea[name='content']").first
textarea.wait_for(state="visible", timeout=10000)
textarea.fill("@bob")
item = page.locator(".mention-dropdown-item:has-text('bob_test')").first
item.wait_for(state="visible", timeout=5000)
expect(item).to_have_class(re.compile(r"\bactive\b"))
def test_mention_tab_inserts_first_match(alice):
page, _ = alice
create_post(page, "random", "Mention tab insert test post")
textarea = page.locator(".comment-form textarea[name='content']").first
textarea.wait_for(state="visible", timeout=10000)
textarea.fill("@bob")
page.locator(".mention-dropdown-item:has-text('bob_test')").first.wait_for(
state="visible", timeout=5000
)
textarea.press("Tab")
expect(textarea).to_have_value(re.compile(r"^@bob_test\s"))
def test_mention_arrow_then_enter_inserts(alice):
page, _ = alice
create_post(page, "random", "Mention arrow nav test post")
textarea = page.locator(".comment-form textarea[name='content']").first
textarea.wait_for(state="visible", timeout=10000)
textarea.fill("@bob")
page.locator(".mention-dropdown-item:has-text('bob_test')").first.wait_for(
state="visible", timeout=5000
)
textarea.press("ArrowDown")
textarea.press("Enter")
expect(textarea).to_have_value(re.compile(r"@bob_test\s"))
def test_mention_escape_closes_dropdown(alice):
page, _ = alice
create_post(page, "random", "Mention escape test post")
textarea = page.locator(".comment-form textarea[name='content']").first
textarea.wait_for(state="visible", timeout=10000)
textarea.fill("@bob")
item = page.locator(".mention-dropdown-item:has-text('bob_test')").first
item.wait_for(state="visible", timeout=5000)
textarea.press("Escape")
expect(item).to_be_hidden()
def test_profile_stars_reflect_content_votes(alice):
page, user = alice
before = _profile_stars(page, user["username"])
create_post(page, "devlog", "Reputation contribution post")
page.locator(".post-action-btn.vote-up").first.click()
with page.expect_response(
lambda r: "/votes/" in r.url and r.request.method == "POST"
):
page.locator(".post-action-btn.vote-up").first.click()
expect(page.locator(".post-vote-count").first).to_have_text("1")
after = _profile_stars(page, user["username"])
assert after == before + 1, f"expected stars {before + 1}, got {after}"
+163
View File
@@ -0,0 +1,163 @@
# retoor <retoor@molodetz.nl>
from uuid import uuid4
from datetime import datetime, timezone
from playwright.sync_api import expect
from tests.conftest import BASE_URL
from devplacepy.database import get_table
from devplacepy.utils import make_combined_slug
BLOCK = "form[action='/block/bob_test'] button"
UNBLOCK = "form[action='/block/unblock/bob_test'] button"
def _confirm(page):
page.locator(".dialog-overlay.visible .dialog-confirm").click()
def _reset_block(page):
page.goto(f"{BASE_URL}/profile/bob_test", wait_until="domcontentloaded")
if page.is_visible(UNBLOCK):
page.click(UNBLOCK)
_confirm(page)
page.wait_for_url("**/profile/bob_test", wait_until="domcontentloaded")
def _seed_user(prefix):
uid = str(uuid4())
get_table("users").insert(
{
"uid": uid,
"username": f"{prefix}_{uid[:8]}",
"email": f"{uid[:8]}@seed.devplace",
"password_hash": "x",
"role": "Member",
"is_active": True,
"created_at": datetime.now(timezone.utc).isoformat(),
}
)
return uid, f"{prefix}_{uid[:8]}"
def _seed_post(owner_uid, marker):
uid = str(uuid4())
get_table("posts").insert(
{
"deleted_at": None,
"deleted_by": None,
"uid": uid,
"user_uid": owner_uid,
"slug": make_combined_slug(marker, uid),
"title": None,
"content": marker,
"topic": "devlog",
"project_uid": None,
"image": None,
"stars": 0,
"created_at": datetime.now(timezone.utc).isoformat(),
}
)
return uid
def test_block_and_unblock_buttons_toggle(alice):
page, _ = alice
_reset_block(page)
assert page.is_visible(BLOCK)
page.click(BLOCK)
_confirm(page)
page.wait_for_url("**/profile/bob_test", wait_until="domcontentloaded")
assert page.is_visible(UNBLOCK)
page.click(UNBLOCK)
_confirm(page)
page.wait_for_url("**/profile/bob_test", wait_until="domcontentloaded")
assert page.is_visible(BLOCK)
def test_block_button_opens_confirmation_dialog(alice):
page, _ = alice
_reset_block(page)
page.click(BLOCK)
expect(page.locator(".dialog-overlay.visible")).to_be_visible()
expect(page.locator(".dialog-overlay.visible .dialog-message")).to_contain_text(
"Block"
)
page.locator(".dialog-overlay.visible .dialog-cancel").click()
expect(page.locator(".dialog-overlay")).not_to_be_visible()
assert page.is_visible(BLOCK)
def test_blocked_authors_post_hidden_from_feed(alice):
page, _ = alice
author_uid, _author = _seed_user("blkfeed")
marker = f"feedmarker{uuid4().hex[:8]}"
_seed_post(author_uid, marker)
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
assert marker in page.content()
resp = page.request.post(
f"{BASE_URL}/block/{_author}", max_redirects=0
)
assert resp.status in (302, 200)
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
assert marker not in page.content()
page.request.post(f"{BASE_URL}/block/unblock/{_author}", max_redirects=0)
def test_blocked_authors_comment_hidden_on_post(alice):
page, _ = alice
host_uid, _host = _seed_user("blkhost")
commenter_uid, commenter = _seed_user("blkcom")
marker_post = f"hostpost{uuid4().hex[:8]}"
post_uid = _seed_post(host_uid, marker_post)
comment_text = f"blockedcomment{uuid4().hex[:8]}"
get_table("comments").insert(
{
"deleted_at": None,
"deleted_by": None,
"uid": str(uuid4()),
"target_type": "post",
"target_uid": post_uid,
"post_uid": post_uid,
"user_uid": commenter_uid,
"content": comment_text,
"parent_uid": None,
"created_at": datetime.now(timezone.utc).isoformat(),
}
)
slug = get_table("posts").find_one(uid=post_uid)["slug"]
page.goto(f"{BASE_URL}/posts/{slug}", wait_until="domcontentloaded")
assert comment_text in page.content()
page.request.post(f"{BASE_URL}/block/{commenter}", max_redirects=0)
page.goto(f"{BASE_URL}/posts/{slug}", wait_until="domcontentloaded")
assert comment_text not in page.content()
page.request.post(f"{BASE_URL}/block/unblock/{commenter}", max_redirects=0)
def test_blocked_users_profile_still_shows_their_content(alice):
page, _ = alice
marker = f"profmarker{uuid4().hex[:8]}"
bob_uid = get_table("users").find_one(username="bob_test")["uid"]
_seed_post(bob_uid, marker)
_reset_block(page)
page.click(BLOCK)
_confirm(page)
page.wait_for_url("**/profile/bob_test", wait_until="domcontentloaded")
page.goto(f"{BASE_URL}/profile/bob_test", wait_until="domcontentloaded")
assert page.is_visible(UNBLOCK)
assert marker in page.content()
page.click(UNBLOCK)
_confirm(page)
page.wait_for_url("**/profile/bob_test", wait_until="domcontentloaded")
+48
View File
@@ -0,0 +1,48 @@
# retoor <retoor@molodetz.nl>
from playwright.sync_api import expect
from tests.conftest import BASE_URL
MUTE = "form[action='/mute/bob_test'] button"
UNMUTE = "form[action='/mute/unmute/bob_test'] button"
def _confirm(page):
page.locator(".dialog-overlay.visible .dialog-confirm").click()
def _reset_mute(page):
page.goto(f"{BASE_URL}/profile/bob_test", wait_until="domcontentloaded")
if page.is_visible(UNMUTE):
page.click(UNMUTE)
_confirm(page)
page.wait_for_url("**/profile/bob_test", wait_until="domcontentloaded")
def test_mute_and_unmute_buttons_toggle(alice):
page, _ = alice
_reset_mute(page)
assert page.is_visible(MUTE)
page.click(MUTE)
_confirm(page)
page.wait_for_url("**/profile/bob_test", wait_until="domcontentloaded")
assert page.is_visible(UNMUTE)
page.click(UNMUTE)
_confirm(page)
page.wait_for_url("**/profile/bob_test", wait_until="domcontentloaded")
assert page.is_visible(MUTE)
def test_mute_button_opens_confirmation_dialog(alice):
page, _ = alice
_reset_mute(page)
page.click(MUTE)
expect(page.locator(".dialog-overlay.visible")).to_be_visible()
expect(page.locator(".dialog-overlay.visible .dialog-message")).to_contain_text(
"Mute"
)
page.locator(".dialog-overlay.visible .dialog-cancel").click()
expect(page.locator(".dialog-overlay")).not_to_be_visible()
assert page.is_visible(MUTE)
+1 -1
View File
@@ -522,7 +522,7 @@ def test_ui_media_lightbox_opens(alice):
thumb.click()
overlay = page.locator(".lightbox-overlay.visible")
overlay.wait_for(state="visible")
assert page.locator(".lightbox-overlay.visible .lightbox-image").is_visible()
expect(page.locator(".lightbox-overlay.visible .lightbox-image")).to_be_visible()
page.locator(".lightbox-overlay.visible .lightbox-close").click()
overlay.wait_for(state="hidden")