"""Integration test against a running local server; browser page is the real face.""" from playwright.sync_api import sync_playwright from pathlib import Path import importlib.util import tempfile import threading import sys ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) spec = importlib.util.spec_from_file_location('kiosk_browser', ROOT / 'kiosk-server.py') kiosk = importlib.util.module_from_spec(spec) spec.loader.exec_module(kiosk) def main(): with tempfile.TemporaryDirectory() as tmp: kiosk.ARQUIVO_APARENCIA = str(Path(tmp) / 'aparencia.json') server = kiosk.Servidor(('127.0.0.1', 0), kiosk.Handler) server.controle = kiosk.ControlePC(Path(tmp) / 'app.json') kiosk.ligar_eventos(server) # o rosto recebe ajustes e ordens por SSE thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() try: browser_test('http://127.0.0.1:' + str(server.server_port)) finally: server.shutdown() server.server_close() thread.join() def browser_test(base): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto(base + '/carinha.html?auto=0&camera=0') page.wait_for_selector('#tela') page.request.post(base + '/app/config', data={'volume': 0.65, 'sompausa': 35, 'reagir': False}) page.wait_for_timeout(1500) assert page.evaluate('window.eilik.audio.volume') == 0.65, 'Volume remoto não aplicado' assert page.evaluate('window.eilik.ouvido().segSilencio') == 35, 'Sono remoto não aplicado' page.evaluate('window.eilik.movimento(0.8, 0.1, 0.5)') assert page.evaluate('window.eilik.presenca().olharX') == 0, 'Reação desligada precisa manter olhos no centro' page.request.post(base+'/app/config', data={'reagir':True}) page.wait_for_timeout(1100) page.evaluate("window.eilik.soltar(); window.eilik.rastrear({tipo:'mao',x:.9,y:.5,atividade:false})") assert page.evaluate('window.eilik.presenca().olharX') > 20, 'Olhos devem seguir alvo em uma amostra' page.evaluate('window.eilik.movimento(.8,.1,.5)') assert page.evaluate('window.eilik.presenca().olharX') > 20, 'Detector simples não pode disputar alvo recente' page.wait_for_timeout(400) page.evaluate('window.eilik.movimento(.8,.1,.5)') assert page.evaluate('window.eilik.presenca().olharX') < 0, 'Alvo expirado deve liberar detector simples' page.evaluate("window.eilik.trata('dormir'); window.eilik.rastrear({tipo:'mao',x:.9,y:.5,atividade:true})") assert page.evaluate('window.eilik.face.dormindo'), 'Sono deve respeitar câmera desativada' page.evaluate('window.eilik.soltar()') page.request.post(base + '/config', data={'rosto': 'miudo', 'cor': [255, 120, 0]}) page.wait_for_timeout(800) assert page.evaluate('window.eilik.rostoAtual()') == 'miudo' assert page.evaluate('window.eilik.face.cor') == [255, 120, 0], 'Cor deve persistir em repouso' page.request.post(base + '/app/config', data={'boca':False, 'sobrancelhas':True}) page.wait_for_timeout(1200) assert not page.locator('#boca').is_visible(), 'Boca deve poder ser desligada' assert page.locator('#sobrancelha1').is_visible(), 'Sobrancelhas devem poder ser ligadas' panel = browser.new_page(viewport={'width': 1440, 'height': 1000}) errors = [] panel.on('pageerror', lambda error: errors.append(str(error) + '\n' + error.stack)) # Painel novo (web/dist): ajustes do celular e transmissão. panel.goto(base + '/#/ajustes') alternar = lambda nome: panel.get_by_role('switch', name=nome).locator('xpath=..').click() alternar('Microfone Só o nível') panel.wait_for_timeout(400) assert panel.request.get(base + '/app/config').json()['sensorMic'] is False alternar('Câmera durante o sono') panel.wait_for_timeout(400) panel.get_by_role('combobox', name='Câmera').select_option('environment') panel.wait_for_timeout(400) c = panel.request.get(base + '/app/config').json() assert c['cameraSono'] is True and c['cameraLado'] == 'environment' panel.get_by_role('link', name='Ao vivo').first.click() alternar('Transmitir câmera do celular') panel.wait_for_timeout(500) assert panel.request.get(base + '/app/config').json()['preview'] is True alternar('Transmitir câmera do celular') panel.wait_for_timeout(500) assert panel.request.get(base + '/app/config').json()['preview'] is False assert not errors, errors work = Path(__file__).resolve().parents[3] / 'work' panel.screenshot(path=str(work / 'painel-desktop.png'), full_page=True) panel.set_viewport_size({'width': 390, 'height': 844}) panel.screenshot(path=str(work / 'painel-mobile.png'), full_page=True) assert panel.evaluate('document.documentElement.scrollWidth <= innerWidth'), 'Overflow mobile' browser.close() print('Remote config, gesture queue, camera toggle and responsive panel: OK') if __name__ == '__main__': main()