fix: refresh occluded browser windows before plugin capture
This commit is contained in:
1 parent
254ce4ffb3
commit
86dab41947
6 files changed
+127
-8
No files matched your search
+51
-3
@@ -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))
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user