"""Chromium about:blank fixtures bridged to real ASGI endpoints; synthetic worker handoffs.
This is not live systemd/Core/SSH qualification. Bootstrap may be a declared
substitute. HTMX is deliberately not loaded when unavailable: status replacements
are driven explicitly and labeled as synthetic lifecycle coverage, not HTMX proof.
Never install fixture assets into production static/vendor.
"""
from pathlib import Path
import argparse
import json
import re
import sys
import tempfile
import time
from urllib.parse import urlsplit
sys.path.insert(0, str(Path(__file__).resolve().parents[1]/'src'))
import pytest
from fastapi.testclient import TestClient
from playwright.sync_api import sync_playwright
from aim_webgui.app import create_app
from aim_webgui.credentials import wire
from test_credential_ux import authz as auth_fixture, make_job, SYNTHETIC
ROOT = Path(__file__).resolve().parents[1]
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--out', type=Path, required=True)
parser.add_argument('--bootstrap', type=Path, default=Path('/opt/imagemagick/share/doc/ImageMagick-7/www/assets/bootstrap.min.css'))
args = parser.parse_args(); args.out.mkdir(parents=True, exist_ok=True)
cases = []
with tempfile.TemporaryDirectory(prefix='aim-credential-qa-') as tmp, pytest.MonkeyPatch.context() as monkey:
auth, users = auth_fixture.__wrapped__(Path(tmp), monkey)
fixture = (auth, users)
packets = []
class Sock:
def __enter__(self): return self
def __exit__(self, *args): pass
monkey.setattr(wire, 'connect', lambda *a, **k: Sock())
monkey.setattr(wire, 'send', lambda sock, packet, limit: packets.append(json.loads(json.dumps(packet))))
def claimed(*args):
ident = packets[-1]['job']
with auth.store.transaction() as db:
db.execute("UPDATE jobs SET credential_phase='claimed' WHERE id=?", (ident,))
return {'accepted': True}
monkey.setattr(wire, 'receive', claimed)
origin = auth.settings.public_url
with TestClient(create_app(auth.settings), base_url=origin, follow_redirects=False) as server, sync_playwright() as pw:
browser = pw.chromium.launch(executable_path='/usr/bin/chromium', args=['--no-sandbox', '--disable-dev-shm-usage'])
try:
for width, height in ((320,750),(390,844),(760,900),(1440,1000),(740,390)):
for theme in ('light','dark'):
# Independent viewport fixtures must not share the live five/minute bucket.
# Production throttle remains enabled and is covered in unit tests.
with auth.store.transaction() as db: db.execute('DELETE FROM rate_limits')
token, session = auth.new_session(users['operator'])
context = browser.new_context(viewport={'width':width,'height':height}, locale='en-GB', timezone_id='Europe/Berlin', reduced_motion='reduce')
context.add_cookies([{'name':auth.settings.cookie_name, 'value':token, 'url':origin,'secure':True,'httpOnly':True,'sameSite':'Lax'}])
errors = []
mode = {'drop_post':False}
def bridge(url, options):
path = urlsplit(url).path
method = options.get('method', 'GET')
if mode['drop_post'] and method == 'POST' and path.endswith('/credentials'):
mode['post_count'] = mode.get('post_count',0)+1
return {'network_error': True}
headers = {**options.get('headers', {}), 'Origin': origin}
response = server.request(method, path, headers=headers, content=options.get('body'))
return {'status': response.status_code, 'body': response.text}
server.cookies.set(auth.settings.cookie_name, token)
page = context.new_page(); page.on('pageerror', lambda e: errors.append(str(e)))
page.expose_function('_fixtureHTTP', bridge)
def mount(path):
page.goto('about:blank')
content = server.get(path).text
content = re.sub(r'', '', content)
content = re.sub(r']+rel="stylesheet"[^>]*>', '', content)
css = args.bootstrap.read_text() + '\n' + '\n'.join((ROOT/'src/aim_webgui/static/css'/n).read_text() for n in ('tokens.css','bootstrap-overrides.css','aim.css','experience.css','credentials.css','evidence.css'))
fixture_js = """
window.fetch = async function(url, options) {
const r=await window._fixtureHTTP(url, options||{});
if(r.network_error) throw new TypeError('Synthetic lost response');
return new Response(r.body,{status:r.status,headers:{'Content-Type':'application/json'}});
};
window.EventSource=class { constructor(){} addEventListener(){} close(){} };
"""
js='\n'.join((ROOT/'src/aim_webgui/static/js'/n).read_text() for n in ('theme.js','aim.js','experience.js','credentials.js','evidence.js'))
content=content.replace('','').replace('