Something went wrong. Try again.
Paper Mario mod hosting site - AT Protocol appview starhaven.dev
appview
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167"""The actor profile page: avatar/bio editing, and the mods grid."""
import re
from playwright.sync_api import expect
from helpers import Actor
PROFILE_COLLECTION = "dev.starhaven.actor.profile"
PNG = bytes.fromhex( "89504e470d0a1a0a0000000d494844520000000100000001080600000" "01f15c4890000000a4944415478da6360000002000155009f490c0000" "000049454e44ae426082")
def bio(actor: Actor): """The bio field on the profile page, once its editor has taken over.""" expect(actor.page.locator(".actor-bio-form .cm-editor")).to_be_visible() return actor.page.get_by_label("Bio")
def test_owner_can_set_an_avatar(actor: Actor): actor.login() actor.page.goto(f"{actor.app_url}/{actor.handle}")
actor.page.locator("#avatar-add").set_input_files( files=[{"name": "avatar.png", "mimeType": "image/png", "buffer": PNG}] ) # The src changes from the default avatar to a blob: URL on success. expect(actor.page.locator("#actor-avatar-img")).to_have_attribute( "src", re.compile(r"^blob:"), timeout=10000 )
# The avatar saves immediately — no form submit needed. [profile] = actor.records(PROFILE_COLLECTION) assert profile["value"]["avatar"]["$type"] == "blob"
def test_owner_can_set_a_bio(actor: Actor): actor.login() actor.page.goto(f"{actor.app_url}/{actor.handle}")
bio(actor).fill("Hi, I make mods.") with actor.page.expect_response(lambda r: r.request.method == "PATCH") as resp: actor.page.get_by_role("button", name="Save").click() assert resp.value.ok
[profile] = actor.records(PROFILE_COLLECTION) assert profile["value"]["description"] == "Hi, I make mods."
def test_owner_can_set_a_display_name_and_the_handle_stays_in_view(actor: Actor): actor.login() actor.set_display_name(" Wry mouth\u202e ")
[profile] = actor.records(PROFILE_COLLECTION) assert profile["value"]["displayName"] == "Wry mouth" expect(actor.page.get_by_role("heading", level=1)).to_have_text("Wry mouth") expect(actor.page.locator(".actor-handle")).to_have_text(f"@{actor.handle}")
def test_a_display_name_is_shown_instead_of_the_handle_on_cards_and_mod_pages( actor: Actor,): actor.login() actor.create_mod("master-quest", title="Master Quest") actor.set_display_name("Wrymouth")
actor.page.goto(f"{actor.app_url}/{actor.handle}/master-quest") credit = actor.page.locator(".mod-credit-name") expect(credit).to_have_text("Wrymouth") expect(credit).to_have_attribute("href", f"/{actor.handle}") expect(actor.page.locator(".mod-credit-handle")).to_have_text(f"@{actor.handle}")
actor.page.goto(f"{actor.app_url}/{actor.handle}") expect(actor.page.locator(".mod-card-byline .byline-names")).to_have_text("Wrymouth")
def test_a_user_can_be_found_by_display_name(actor: Actor): actor.login() actor.set_display_name("Wrymouth")
actor.page.goto(f"{actor.app_url}/search?q=wrymouth&type=users") expect(actor.page.locator(".search-users")).to_contain_text("Wrymouth") expect(actor.page.locator(".search-users")).to_contain_text(f"@{actor.handle}")
def test_a_display_name_that_is_too_long_is_refused(actor: Actor): actor.login() actor.page.goto(f"{actor.app_url}/{actor.handle}") # The field's own limit would stop this being typed. actor.page.locator("#display-name").evaluate("el => el.removeAttribute('maxlength')") actor.page.locator("#display-name").fill("a" * 65) with actor.page.expect_response(lambda r: r.request.method == "PATCH") as resp: actor.page.get_by_role("button", name="Save").click() assert resp.value.status == 400 assert actor.records(PROFILE_COLLECTION) == []
def test_avatar_persists_after_bio_save(actor: Actor): """Setting a bio must not erase a previously-uploaded avatar.""" actor.login() actor.page.goto(f"{actor.app_url}/{actor.handle}")
actor.page.locator("#avatar-add").set_input_files( files=[{"name": "avatar.png", "mimeType": "image/png", "buffer": PNG}] ) expect(actor.page.locator("#actor-avatar-img")).to_have_attribute( "src", re.compile(r"^blob:"), timeout=10000 )
bio(actor).fill("Still has avatar") with actor.page.expect_response(lambda r: r.request.method == "PATCH") as resp: actor.page.get_by_role("button", name="Save").click() assert resp.value.ok
[profile] = actor.records(PROFILE_COLLECTION) assert profile["value"]["avatar"]["$type"] == "blob" assert profile["value"]["description"] == "Still has avatar"
def test_a_visitor_sees_no_edit_controls(actor: Actor): actor.login() actor.page.goto(f"{actor.app_url}/{actor.handle}") bio(actor).fill("My bio") # Saving redirects back to the profile, which has to land before the # next navigation or it interrupts it. with actor.page.expect_navigation(): with actor.page.expect_response(lambda r: r.request.method == "PATCH") as resp: actor.page.get_by_role("button", name="Save").click() assert resp.value.ok actor.page.context.clear_cookies()
actor.page.goto(f"{actor.app_url}/{actor.handle}") expect(actor.page.get_by_text("My bio")).to_be_visible() expect(actor.page.locator("#avatar-add")).to_have_count(0) expect(actor.page.locator("#profile-form")).to_have_count(0)
def test_a_bio_shows_inline_formatting_and_links(actor: Actor): actor.login() actor.page.goto(f"{actor.app_url}/{actor.handle}") bio(actor).fill("I make **mods**.\nSee https://example.com/mine") with actor.page.expect_navigation(): with actor.page.expect_response(lambda r: r.request.method == "PATCH") as resp: actor.page.get_by_role("button", name="Save").click() assert resp.value.ok actor.page.context.clear_cookies()
actor.page.goto(f"{actor.app_url}/{actor.handle}") shown = actor.page.locator(".actor-bio") expect(shown.locator("strong")).to_have_text("mods") expect(shown.get_by_role("link", name="example.com/mine")).to_have_attribute( "href", "https://example.com/mine" )
def test_the_actors_mods_show_as_cards(actor: Actor): actor.login() actor.create_mod("master-quest", title="Master Quest")
actor.page.goto(f"{actor.app_url}/{actor.handle}") expect(actor.page.locator("ul.mod-grid")).to_be_visible() expect(actor.page.get_by_role("link", name="Master Quest")).to_be_visible()