mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
75 lines
3.8 KiB
Python
75 lines
3.8 KiB
Python
"""Container-scoped submit tier: the middle rung between the below-composer index pick and the
|
||
by-name 'Send' last resort, plus the expression builder's escaping. The JS itself is proven live
|
||
(X modal resolves tweetButton, inline resolves tweetButtonInline at hop 20); these tests pin the
|
||
tier ORDER and the fail-safe parse."""
|
||
import pytest
|
||
|
||
from backend.apps.agents.browser import browser_send_script as ss
|
||
from backend.apps.agents.browser import browser_submit_click as sc
|
||
from backend.apps.agents.browser.browser_agent import send_submit_index_in_state
|
||
|
||
# Committed fill whose submit ranks OUT of the capped listing (the shape that starves the picker).
|
||
COMPOSER_FILLED_NO_SEND = '[1]<textbox "I\'m looking for…">\n[24]<textbox "Write a message" value="[test] hello world r9-os">'
|
||
COMPOSER_SENT = '[2]<textbox "Write a message">\n[9]<button "Attach">'
|
||
|
||
|
||
def make_exec(eval_result):
|
||
calls = {"clicks": []}
|
||
|
||
async def execute(tool, params, bid, tid):
|
||
if tool == "BrowserListInteractives":
|
||
return {"text": COMPOSER_SENT}
|
||
calls["clicks"].append((tool, params))
|
||
if tool == "BrowserEvaluate":
|
||
return eval_result
|
||
return {"ok": True}
|
||
return execute, calls
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_container_submit_tier_between_index_and_by_name():
|
||
"""X's compose modal: covered feed rows behind the overlay eat the 60-row cap, so the modal's
|
||
own Post never reaches the picker (live 0/2 deliveries). The container tier resolves the
|
||
submit inside the composer's own container and clicks it with REAL input; by-name stays last."""
|
||
execute, calls = make_exec({"value": {"ok": True, "name": "post", "xPct": 61.0, "yPct": 33.0}})
|
||
r = await ss.complete_send("[test] hello world r9-os", COMPOSER_FILLED_NO_SEND,
|
||
"b1", "", execute, send_submit_index_in_state, composer_index=24)
|
||
assert r["clicked"] is True and r["sent"] is True
|
||
tools = [c[0] for c in calls["clicks"]]
|
||
assert "BrowserEvaluate" in tools and "BrowserClickByName" not in tools
|
||
point = [c for c in calls["clicks"] if c[0] == "BrowserClickPoint"]
|
||
assert point and point[0][1] == {"xPercent": 61.0, "yPercent": 33.0}
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_container_submit_miss_falls_to_by_name():
|
||
execute, calls = make_exec({"value": {"ok": False, "why": "no submit control in the composer container"}})
|
||
r = await ss.complete_send("[test] hello world r9-os", COMPOSER_FILLED_NO_SEND,
|
||
"b1", "", execute, send_submit_index_in_state, composer_index=24)
|
||
assert r["clicked"] is True
|
||
tools = [c[0] for c in calls["clicks"]]
|
||
assert tools.index("BrowserEvaluate") < tools.index("BrowserClickByName")
|
||
|
||
|
||
def test_container_submit_expression_escapes_and_truncates():
|
||
expr = sc.container_submit_expression('he said "hi" ' + "x" * 60)
|
||
assert '\\"hi\\"' in expr # payload lands as a JS string literal, quotes escaped
|
||
assert "x" * 30 not in expr # truncated to the 24-char prefix the fill verifier uses
|
||
assert '"post"' in expr and '"send"' in expr
|
||
|
||
|
||
def test_clean_button_name_strips_shortcut_suffix_and_bidi():
|
||
# Gmail's real Send accessible name: 'Send (⌘Enter)'
|
||
assert sc.clean_button_name("Send (⌘Enter)") == "send"
|
||
assert sc.clean_button_name(" Post ") == "post"
|
||
assert sc.clean_button_name("Reply (Ctrl-Enter)") == "reply"
|
||
assert sc.clean_button_name("") == ""
|
||
|
||
|
||
def test_parse_eval_value_reads_value_text_and_garbage():
|
||
assert sc.parse_eval_value({"value": {"ok": True}}) == {"ok": True}
|
||
assert sc.parse_eval_value({"text": '{"ok": false, "why": "x"}'}) == {"ok": False, "why": "x"}
|
||
assert sc.parse_eval_value({"text": "not json"}) is None
|
||
assert sc.parse_eval_value({"error": "boom"}) is None
|
||
assert sc.parse_eval_value("weird") is None
|