104 lines
4.5 KiB
Python
104 lines
4.5 KiB
Python
"""Muji (XEP-0272) group call between three browsers in a group chat."""
|
|
import asyncio, time
|
|
from playwright.async_api import async_playwright
|
|
import sys
|
|
from browser import login, BASE, OUT
|
|
|
|
VIDEO = 'true' if '--video' in sys.argv else 'false'
|
|
|
|
|
|
async def open_room(page, room):
|
|
await page.evaluate(f"Chat_ajaxGetRoom('{room}')")
|
|
await page.wait_for_selector("#chat_textarea", timeout=15000)
|
|
await page.wait_for_timeout(1500)
|
|
|
|
|
|
async def join_from_lobby(page):
|
|
await page.wait_for_selector("#lobby_start:not(.disabled)", timeout=15000)
|
|
await page.click("#lobby_start")
|
|
|
|
|
|
async def sessions(page):
|
|
return await page.evaluate(
|
|
"Object.fromEntries(Object.entries(MovimJingles.sessions || {}).map(([j, s]) => [j, s.pc && s.pc.connectionState]))"
|
|
)
|
|
|
|
|
|
async def main():
|
|
room = f"call{int(time.time())}@conference.localhost"
|
|
async with async_playwright() as p:
|
|
browser = await p.chromium.launch(args=["--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream"])
|
|
users = [("alice", "alicepass"), ("bob", "bobpass"), ("carol", "carolpass")]
|
|
pages, errors = {}, []
|
|
for name, password in users:
|
|
ctx, page, err = await login(browser, f"{name}@localhost", password, f"muji{name}")
|
|
await ctx.grant_permissions(["microphone", "camera"], origin=BASE)
|
|
pages[name] = page
|
|
await page.evaluate("""
|
|
window.addEventListener('unhandledrejection', e => console.error('REJECTION', e.reason && e.reason.stack));
|
|
window.addEventListener('error', e => console.error('ERROR', e.error && e.error.stack));
|
|
""")
|
|
errors.append(err)
|
|
alice, bob, carol = pages["alice"], pages["bob"], pages["carol"]
|
|
|
|
# Alice creates a group chat and invites the others, who join it
|
|
await alice.evaluate(f"""RoomsUtils_ajaxAddCreate({{
|
|
jid: {{value: '{room}'}}, name: {{value: 'Call room'}}, nick: {{value: 'alice'}},
|
|
type: {{value: 'groupchat'}}, autojoin: {{value: true}}, pinned: {{value: false}},
|
|
notify: {{value: 'always'}}
|
|
}})""")
|
|
await alice.wait_for_timeout(3000)
|
|
for name in ("bob", "carol"):
|
|
await alice.evaluate(f"RoomsUtils_ajaxInvite({{to: {{value: '{room}'}}, invite: {{value: '{name}@localhost'}}}})")
|
|
await alice.wait_for_timeout(1500)
|
|
for name in ("bob", "carol"):
|
|
await pages[name].evaluate(f"""RoomsUtils_ajaxAddConfirm({{
|
|
jid: {{value: '{room}'}}, name: {{value: 'Call room'}}, nick: {{value: '{name}'}},
|
|
autojoin: {{value: true}}, pinned: {{value: false}}, notify: {{value: 'on-mention'}}
|
|
}})""")
|
|
await alice.wait_for_timeout(3000)
|
|
for page in pages.values():
|
|
await open_room(page, room)
|
|
await alice.screenshot(path=f"{OUT}/muji_0_header.png")
|
|
|
|
# Alice starts the call
|
|
await alice.evaluate(f"Visio_ajaxGetMujiLobby('{room}', true, {VIDEO})")
|
|
await join_from_lobby(alice)
|
|
await alice.wait_for_timeout(3000)
|
|
await alice.screenshot(path=f"{OUT}/muji_1_alice_waiting.png")
|
|
await bob.screenshot(path=f"{OUT}/muji_1_bob_header.png")
|
|
print("bob sees a call to join:", await bob.locator("button.oppose.color.blue").count() > 0)
|
|
|
|
# Bob then Carol join
|
|
for name, page in (("bob", bob), ("carol", carol)):
|
|
button = page.locator("button.oppose.color.blue").first
|
|
try:
|
|
await button.click(timeout=10000)
|
|
except Exception:
|
|
await page.screenshot(path=f"{OUT}/muji_fail_{name}.png")
|
|
print(name, "header:", await page.content())
|
|
raise
|
|
await join_from_lobby(page)
|
|
await page.wait_for_timeout(6000)
|
|
print(f"after {name} joined:", {n: await sessions(pg) for n, pg in pages.items()})
|
|
|
|
for name, page in pages.items():
|
|
await page.screenshot(path=f"{OUT}/muji_2_{name}.png")
|
|
print(name, "sessions:", await sessions(page))
|
|
|
|
print("JS errors before leaving:", sum(len(e) for e in errors))
|
|
# Carol leaves, the others keep talking
|
|
await carol.evaluate(f"Visio_ajaxLeaveMuji('{room}')")
|
|
await alice.wait_for_timeout(3000)
|
|
print("after carol left, alice sessions:", await sessions(alice))
|
|
print("carol sessions:", await sessions(carol))
|
|
await alice.screenshot(path=f"{OUT}/muji_3_alice.png")
|
|
|
|
for err in errors:
|
|
for e in err:
|
|
print(e)
|
|
await browser.close()
|
|
|
|
|
|
asyncio.run(main())
|