"""Opt-in isolated Chrome form test against the packaged Windows MCP server."""
import argparse
import asyncio
import json
import re
import subprocess
import tempfile
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
import psutil
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main(executable, chrome):
received = []
class Handler(BaseHTTPRequestHandler):
def log_message(self, *args):
pass
def do_GET(self):
body = b"""
ARTEMIS Browser Fixture
ARTEMIS Browser Fixture
Pending
"""
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(body)
def do_POST(self):
received.append(
self.rfile.read(int(self.headers["Content-Length"])).decode()
)
self.send_response(200)
self.end_headers()
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
threading.Thread(target=server.serve_forever, daemon=True).start()
with tempfile.TemporaryDirectory(prefix="artemis-browser-smoke-") as folder:
process = await asyncio.create_subprocess_exec(
*[
str(chrome),
"--user-data-dir=" + folder,
"--no-first-run",
"--no-default-browser-check",
"--disable-extensions",
"--app=http://127.0.0.1:" + str(server.server_port),
"--window-size=800,600",
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NO_WINDOW,
)
try:
await asyncio.sleep(3)
async with (
stdio_client(
StdioServerParameters(command=str(executable), args=["mcp"])
) as streams,
ClientSession(*streams) as session,
):
await session.initialize()
apps = await session.call_tool("list_apps", {})
own = next(
b.text
for b in apps.content
if b.type == "text" and "ARTEMIS Browser Fixture" in b.text
)
line = next(
line
for line in own.splitlines()
if "ARTEMIS Browser Fixture" in line
)
pid = re.search(r"pid=(\d+)", line).group(1)
async def observe():
response = await session.call_tool("get_app_state", {"app": pid})
assert not response.isError, response
content = "\n".join(
b.text for b in response.content if b.type == "text"
)
assert any(b.type == "image" for b in response.content), (
"Browser capture unavailable"
)
return content
state = await observe()
print(state)
entry = re.search(
r"^\s*(\d+) (?:entry|text field|edit|textbox|编辑)[^\n]*Test parameter",
state,
re.MULTILINE | re.IGNORECASE,
)
if entry is None:
raise AssertionError("No editable browser form control exposed")
result = await session.call_tool(
"set_value",
{
"app": pid,
"element_index": entry.group(1),
"value": "ARTEMIS 中文参数",
},
)
assert not result.isError, result
state = await observe()
button = re.search(
r"^\s*(\d+) (?:button|按钮) Apply parameter",
state,
re.MULTILINE | re.IGNORECASE,
)
assert button, state
result = await session.call_tool(
"click",
{
"app": pid,
"element_index": button.group(1),
"click_method": "auto",
},
)
assert not result.isError, result
state = await observe()
assert received == ["ARTEMIS 中文参数"], json.dumps(received)
assert "Saved: ARTEMIS 中文参数" in state
print(
"PASS: isolated browser form changed, submitted once and read back through MCP"
)
finally:
try:
parent = psutil.Process(process.pid)
children = parent.children(recursive=True)
for child in children:
try:
child.terminate()
except psutil.NoSuchProcess:
pass
parent.terminate()
psutil.wait_procs([parent, *children], timeout=10)
except psutil.NoSuchProcess:
pass
server.shutdown()
server.server_close()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("executable", type=Path)
parser.add_argument("--chrome", type=Path, required=True)
options = parser.parse_args()
asyncio.run(main(options.executable.resolve(), options.chrome))