fix: refresh occluded browser windows before plugin capture

This commit is contained in:
chenmingxuan committed 2026-10-14 20:17:59 +08:00
1 parent 254ce4ffb3
commit 86dab41947
6 files changed
+127 -8

No files matched your search

+51 -3
View File
@@ -2,6 +2,8 @@
import argparse
import asyncio
import base64
import io
import json
import re
import subprocess
@@ -13,9 +15,10 @@ from pathlib import Path
import psutil
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from PIL import Image
async def main(executable, chrome):
async def main(executable, chrome, occlude=False):
received = []
class Handler(BaseHTTPRequestHandler):
@@ -26,7 +29,7 @@ async def main(executable, chrome):
body = b"""<!doctype html><meta charset="utf-8"><title>ARTEMIS Browser Fixture</title>
<style>body{font:24px sans-serif;margin:40px}input,button{font:inherit;padding:12px;display:block;margin:20px 0}</style>
<h1>ARTEMIS Browser Fixture</h1><label>Test parameter<input aria-label="Test parameter" id="value"></label>
<button aria-label="Apply parameter" onclick="fetch('/save',{method:'POST',body:document.getElementById('value').value}).then(()=>document.getElementById('result').textContent='Saved: '+document.getElementById('value').value)">Apply parameter</button>
<button aria-label="Apply parameter" onclick="fetch('/save',{method:'POST',body:document.getElementById('value').value}).then(()=>{document.getElementById('result').textContent='Saved: '+document.getElementById('value').value;document.body.style.backgroundColor='#10e020'})">Apply parameter</button>
<div id="result" aria-live="polite">Pending</div>"""
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
@@ -52,13 +55,40 @@ async def main(executable, chrome):
"--disable-extensions",
"--app=http://127.0.0.1:" + str(server.server_port),
"--window-size=800,600",
"--window-position=100,100",
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NO_WINDOW,
)
cover = None
try:
await asyncio.sleep(3)
if occlude:
ready = Path(folder) / "cover.ready"
cover = await asyncio.create_subprocess_exec(
"powershell.exe",
"-NoProfile",
"-NonInteractive",
"-File",
str(Path(__file__).with_name("capture-fixture.ps1")),
"-Title",
"ARTEMIS Browser Occlusion Fixture",
"-Cover",
"-Width",
"1000",
"-Height",
"800",
"-ReadyFile",
str(ready),
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NO_WINDOW,
)
async with asyncio.timeout(10):
while not ready.exists():
await asyncio.sleep(0.05)
await asyncio.sleep(1)
async with (
stdio_client(
StdioServerParameters(command=str(executable), args=["mcp"])
@@ -79,6 +109,8 @@ async def main(executable, chrome):
)
pid = re.search(r"pid=(\d+)", line).group(1)
captured = []
async def observe():
response = await session.call_tool("get_app_state", {"app": pid})
assert not response.isError, response
@@ -88,6 +120,9 @@ async def main(executable, chrome):
assert any(b.type == "image" for b in response.content), (
"Browser capture unavailable"
)
captured.append(
next(b.data for b in response.content if b.type == "image")
)
return content
state = await observe()
@@ -127,10 +162,22 @@ async def main(executable, chrome):
state = await observe()
assert received == ["ARTEMIS 中文参数"], json.dumps(received)
assert "Saved: ARTEMIS 中文参数" in state
with Image.open(
io.BytesIO(base64.b64decode(captured[-1]))
) as screenshot:
pixels = list(screenshot.convert("RGB").getdata())
green = sum(g > 180 and r < 80 and b < 80 for r, g, b in pixels)
assert green > len(pixels) / 3, (
"Screenshot did not reflect the submitted page change"
)
print(
"PASS: isolated browser form changed, submitted once and read back through MCP"
)
finally:
if cover:
if cover.returncode is None:
cover.terminate()
await asyncio.wait_for(cover.wait(), 10)
try:
parent = psutil.Process(process.pid)
children = parent.children(recursive=True)
@@ -151,5 +198,6 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("executable", type=Path)
parser.add_argument("--chrome", type=Path, required=True)
parser.add_argument("--occlude", action="store_true")
options = parser.parse_args()
asyncio.run(main(options.executable.resolve(), options.chrome))
asyncio.run(main(options.executable.resolve(), options.chrome, options.occlude))
+2 -2
View File
@@ -1,9 +1,9 @@
param([string]$Title, [string]$ReadyFile, [switch]$Cover)
param([string]$Title, [string]$ReadyFile, [switch]$Cover, [int]$Width=500, [int]$Height=300)
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = $Title
$form.Size = New-Object System.Drawing.Size(500, 300)
$form.Size = New-Object System.Drawing.Size($Width, $Height)
$form.StartPosition = 'Manual'
$form.Location = New-Object System.Drawing.Point(100, 100)
$form.TopMost = $true