266 lines
22 KiB
Python
266 lines
22 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""Regresión de selectores mediante el MCP/Chromium del preparador sintético existente.
|
|||
|
|
|
|||
|
|
METIS_PLAYWRIGHT_STORAGE_STATE=.artifacts/playwright/authenticated-ID/session.json \
|
|||
|
|
python3 scripts/picker-ui.test.py
|
|||
|
|
No inicia servicios ni escribe entidades. La fixture React solo se importa por Vite en tests.
|
|||
|
|
"""
|
|||
|
|
import contextlib
|
|||
|
|
import json
|
|||
|
|
import os
|
|||
|
|
from pathlib import Path
|
|||
|
|
import runpy
|
|||
|
|
import tomllib
|
|||
|
|
import unittest
|
|||
|
|
from urllib.parse import quote, urlparse
|
|||
|
|
|
|||
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|||
|
|
AUDIT = runpy.run_path(str(ROOT / "scripts/visual-audit.py"))
|
|||
|
|
SIZES = [(320, 640), (390, 844), (768, 1024), (1024, 768), (1366, 768), (1440, 900)]
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PickerUiTests(unittest.TestCase):
|
|||
|
|
@classmethod
|
|||
|
|
def setUpClass(cls):
|
|||
|
|
value = os.environ.get("METIS_PLAYWRIGHT_STORAGE_STATE")
|
|||
|
|
if not value:
|
|||
|
|
raise unittest.SkipTest("Requiere sesión sintética activa de scripts/visual-audit.py.")
|
|||
|
|
state = (ROOT / value).resolve()
|
|||
|
|
if not state.is_relative_to(ROOT / ".artifacts/playwright") or state.stat().st_mode & 0o077:
|
|||
|
|
raise RuntimeError("La sesión debe ser privada y pertenecer al directorio de auditoría.")
|
|||
|
|
report = json.loads((state.parent / "report.json").read_text())
|
|||
|
|
if urlparse(report['origin']).hostname != 'localhost' or report.get('ordinaryUser') is not True:
|
|||
|
|
raise RuntimeError("Se requiere una sesión sintética local ordinaria.")
|
|||
|
|
cls.origin = report['origin']
|
|||
|
|
cls.company = '/companies/' + report['companyId']
|
|||
|
|
cls.asset_id = report['assetId']
|
|||
|
|
cls.output = ROOT / '.artifacts/playwright/l02-1'
|
|||
|
|
cls.output.mkdir(parents=True, exist_ok=True)
|
|||
|
|
cls.results = []
|
|||
|
|
cls.measurements = cls.output / ('measurements-' + str(os.getpid()) + '.json')
|
|||
|
|
cls.stack = contextlib.ExitStack()
|
|||
|
|
cls.addClassCleanup(cls.stack.close)
|
|||
|
|
config = tomllib.loads((ROOT / '.codex/config.toml').read_text())['mcp_servers']['playwright']
|
|||
|
|
cls.client = cls.stack.enter_context(AUDIT['browser'](config, state))
|
|||
|
|
status=cls.code('return (await page.request.get('+json.dumps(cls.origin+'/api/companies')+')).status();')
|
|||
|
|
if status != 200:
|
|||
|
|
raise RuntimeError('La sesión sintética no está activa; preparar otra según docs/playwright-mcp.md.')
|
|||
|
|
cls.code("page.blockedWrites=[];await page.route('**/api/**',r=>{if(['GET','HEAD','OPTIONS'].includes(r.request().method()))return r.continue();page.blockedWrites.push(r.request().method());return r.abort()});return true;")
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def tool(cls, name, args):
|
|||
|
|
return AUDIT['tool'](cls.client, name, args)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def code(cls, body):
|
|||
|
|
return AUDIT['result_json'](cls.tool('browser_run_code_unsafe', {'code': 'async(page)=>{' + body + '}'}))
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def tearDownClass(cls):
|
|||
|
|
(cls.output / 'console.json').write_text(json.dumps(cls.tool('browser_console_messages', {'level': 'error'}), ensure_ascii=False, indent=2)+'\n')
|
|||
|
|
(cls.output / 'blocked-writes.json').write_text(json.dumps(cls.code('return page.blockedWrites;'))+'\n')
|
|||
|
|
|
|||
|
|
def settle(self):
|
|||
|
|
self.code("await page.evaluate(async()=>{await document.fonts.ready;await new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r)));await Promise.all(document.getAnimations().filter(a=>a.effect?.getTiming().iterations!==Infinity).map(a=>a.finished.catch(()=>{})))});return true;")
|
|||
|
|
|
|||
|
|
def resize(self, w, h):
|
|||
|
|
self.tool('browser_resize', {'width': w, 'height': h})
|
|||
|
|
self.settle()
|
|||
|
|
|
|||
|
|
def navigate(self, path):
|
|||
|
|
self.code("await page.evaluate(()=>window.pickerTestRoot?.unmount());return true;")
|
|||
|
|
self.tool('browser_navigate', {'url': self.origin+path})
|
|||
|
|
self.code("await page.locator('[data-slot=workspace]').waitFor();return true;")
|
|||
|
|
self.settle()
|
|||
|
|
|
|||
|
|
def record(self, name, value):
|
|||
|
|
self.results.append({'scenario': name, 'value': value})
|
|||
|
|
self.measurements.write_text(json.dumps(self.results, ensure_ascii=False, indent=2)+'\n')
|
|||
|
|
|
|||
|
|
def capture(self, name):
|
|||
|
|
AUDIT['capture'](self.client, self.output, name)
|
|||
|
|
result = self.tool('browser_snapshot', {})
|
|||
|
|
(self.output / (name+'.txt')).write_text('\n'.join(v['text'] for v in result['content'] if v.get('type') == 'text'))
|
|||
|
|
|
|||
|
|
def fixture(self, count=12, locale='es', long_names=False, kind='fields'):
|
|||
|
|
self.navigate('/companies')
|
|||
|
|
module = '/@fs/' + quote(str(ROOT / 'packages/rdx-ui/src/picker-browser-fixture.tsx'), safe='/')
|
|||
|
|
options = {'count': count, 'locale': locale, 'longNames': long_names, 'kind': kind}
|
|||
|
|
self.code('const target=await page.evaluateHandle(()=>{const d=document.createElement("div");document.body.append(d);return d});await page.evaluate(async({target,options,module})=>{const {mountPickerFixture}=await import(module);window.pickerTestRoot=mountPickerFixture(target,options)},{target,options:'+json.dumps(options)+',module:'+json.dumps(module)+'});return true;')
|
|||
|
|
self.code("await page.getByRole('button',{name:"+json.dumps('Choose fields' if locale == 'en' else 'Seleccionar campos')+",exact:true}).click();await page.getByRole('dialog').locator('input[data-quick-search]').waitFor();return true;")
|
|||
|
|
self.settle()
|
|||
|
|
|
|||
|
|
def geometry(self):
|
|||
|
|
return self.code("""return await page.locator('[role=dialog]:has([data-slot=form-body])').last().evaluate(d=>{
|
|||
|
|
let c=d.querySelector('[data-slot=table-container]');for(let n=c.parentElement;n&&n!==d;n=n.parentElement){if(getComputedStyle(n).overflowY==='auto'&&n.clientHeight<c.clientHeight)c=n}const b=c.getBoundingClientRect(),head=d.querySelector('thead').getBoundingClientRect();
|
|||
|
|
const body=d.querySelector('[data-slot=form-body]'),br=body.getBoundingClientRect(),f=d.querySelector('[data-slot=dialog-footer]').getBoundingClientRect(),dr=d.getBoundingClientRect();
|
|||
|
|
const bottom=Math.min(b.bottom,br.bottom,f.top),top=Math.max(b.top,br.top,head.bottom);
|
|||
|
|
const rows=[...d.querySelectorAll('tbody tr')].filter(e=>e.querySelector('[role=checkbox],[role=radio]'));
|
|||
|
|
return {viewport:[innerWidth,innerHeight],height:dr.height,tableClient:c.clientHeight,tableScroll:c.scrollHeight,bodyClient:body.clientHeight,bodyScroll:body.scrollHeight,full:rows.filter(e=>{const r=e.getBoundingClientRect();return r.top>=top-.5&&r.bottom<=bottom+.5}).length,rows:rows.length,footerVisible:f.top>=dr.top&&f.bottom<=dr.bottom+.5,focusQuick:document.activeElement.hasAttribute('data-quick-search'),overflow:document.documentElement.scrollWidth>innerWidth};
|
|||
|
|
});""")
|
|||
|
|
|
|||
|
|
def test_01_capacity_counts_and_resize(self):
|
|||
|
|
for count in [0, 1, 2, 12]:
|
|||
|
|
with self.subTest(count=count):
|
|||
|
|
self.resize(320, 640)
|
|||
|
|
self.fixture(count=count)
|
|||
|
|
g=self.geometry();self.record('count-'+str(count),g)
|
|||
|
|
self.assertGreater(g['tableClient'], 0)
|
|||
|
|
self.assertTrue(g['footerVisible'])
|
|||
|
|
self.assertTrue(g['focusQuick'])
|
|||
|
|
self.assertFalse(g['overflow'])
|
|||
|
|
if count < 3:self.assertEqual(g['full'],count)
|
|||
|
|
if count>=2:self.assertGreaterEqual(g['full'],2)
|
|||
|
|
self.code("await page.keyboard.press('Escape');return true;")
|
|||
|
|
for kind in ['fields','options']:
|
|||
|
|
self.fixture(kind=kind)
|
|||
|
|
for w,h in SIZES:
|
|||
|
|
self.resize(w,h);g=self.geometry();self.record(f'{kind}-{w}',g)
|
|||
|
|
self.assertGreater(g['tableClient'],0)
|
|||
|
|
self.assertGreaterEqual(g['full'],2)
|
|||
|
|
self.assertTrue(g['footerVisible'])
|
|||
|
|
self.assertFalse(g['overflow'])
|
|||
|
|
if w in [320,1440]:self.capture(f'after-fixture-{kind}-{w}')
|
|||
|
|
self.code("await page.keyboard.press('Escape');return true;")
|
|||
|
|
|
|||
|
|
def test_02_long_help_and_short_window(self):
|
|||
|
|
self.resize(320,640);self.fixture(long_names=True)
|
|||
|
|
g=self.geometry();self.record('long-names',g);self.assertGreater(g['tableClient'],0)
|
|||
|
|
self.code("await page.getByRole('dialog').locator('[data-slot=table-container]').evaluate(e=>e.scrollTop=e.scrollHeight);return true;")
|
|||
|
|
self.settle();self.capture('after-long-names-scrolled')
|
|||
|
|
self.code("await page.getByRole('button',{name:'Sección y ayuda',exact:true}).click();await page.locator('[data-slot=popover-content]').waitFor();return true;")
|
|||
|
|
self.settle()
|
|||
|
|
help_metrics=self.code("return await page.locator('[data-slot=popover-content]').evaluate(e=>({client:e.clientHeight,scroll:e.scrollHeight,top:e.getBoundingClientRect().top,bottom:e.getBoundingClientRect().bottom,text:e.textContent}));")
|
|||
|
|
self.record('long-help',help_metrics);self.assertGreater(help_metrics['client'],0)
|
|||
|
|
self.assertGreater(help_metrics['scroll'],help_metrics['client'])
|
|||
|
|
self.assertIn('Sección de destino',help_metrics['text'])
|
|||
|
|
self.code("await page.keyboard.press('PageDown');await page.waitForFunction(()=>document.querySelector('[data-slot=popover-content]').scrollTop>0);return true;")
|
|||
|
|
self.record('help-keyboard-scroll',self.code("return await page.locator('[data-slot=popover-content]').evaluate(e=>({scroll:e.scrollTop,focused:e.contains(document.activeElement)}));"))
|
|||
|
|
self.assertEqual(self.geometry()['height'],g['height'])
|
|||
|
|
self.capture('after-help-320')
|
|||
|
|
self.code("await page.keyboard.press('Escape');await page.locator('[data-slot=popover-content]').waitFor({state:'hidden'});return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').count();"),1)
|
|||
|
|
self.resize(320,480);self.record('short-window',self.geometry());self.assertGreater(self.geometry()['tableClient'],0)
|
|||
|
|
self.capture('after-short-window')
|
|||
|
|
self.code("await page.keyboard.press('Escape');return true;")
|
|||
|
|
|
|||
|
|
def test_03_identity_draft_keyboard_and_paging(self):
|
|||
|
|
self.resize(390,844);self.fixture(kind='options')
|
|||
|
|
self.code("await page.getByRole('dialog').getByRole('button',{name:'Columnas',exact:true}).click();return true;")
|
|||
|
|
states=self.code("return await page.getByRole('dialog').last().getByRole('checkbox').evaluateAll(es=>es.map(e=>({label:e.closest('label').textContent,disabled:e.getAttribute('aria-disabled')==='true'||e.hasAttribute('disabled')})));")
|
|||
|
|
self.record('protected-columns',states)
|
|||
|
|
self.assertTrue(next(x for x in states if x['label']=='Campo')['disabled'])
|
|||
|
|
self.assertTrue(next(x for x in states if x['label']=='Selección')['disabled'])
|
|||
|
|
self.code("await page.getByRole('dialog').last().getByRole('checkbox',{name:'Detalles',exact:true}).uncheck();await page.keyboard.press('Escape');await page.getByRole('dialog').getByRole('radio').first().focus();await page.keyboard.press('Space');return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').getByRole('radio').first().getAttribute('aria-checked');"),'true')
|
|||
|
|
self.assertEqual(self.code("return await page.locator('output').textContent();"),'—')
|
|||
|
|
self.capture('after-option-identity')
|
|||
|
|
self.code("await page.getByRole('button',{name:'Cancelar',exact:true}).click();return true;")
|
|||
|
|
self.assertTrue(self.code("return await page.getByRole('button',{name:'Seleccionar campos',exact:true}).evaluate(e=>e===document.activeElement);"))
|
|||
|
|
self.code("await page.getByRole('button',{name:'Seleccionar campos',exact:true}).click();return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').getByRole('radio').first().getAttribute('aria-checked');"),'false')
|
|||
|
|
self.code("await page.getByRole('dialog').getByRole('radio').first().check();await page.getByRole('dialog').getByRole('button',{name:'Página siguiente',exact:true}).click();return true;")
|
|||
|
|
self.assertEqual(self.geometry()['rows'],2)
|
|||
|
|
self.code("await page.getByRole('dialog').getByRole('button',{name:'Seleccionar',exact:true}).click();return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.locator('output').textContent();"),'option-0')
|
|||
|
|
|
|||
|
|
def test_04_catalogs_actual_nested_and_empty(self):
|
|||
|
|
for module in ['assets','resources']:
|
|||
|
|
self.resize(320,640);self.navigate('/catalogs/'+module)
|
|||
|
|
self.code("await page.getByRole('button',{name:'Nueva plantilla',exact:true}).click();await page.getByRole('dialog').getByRole('button',{name:/^Añadir campo/}).click();return true;")
|
|||
|
|
self.settle();initial=self.geometry();self.record('catalog-'+module,initial)
|
|||
|
|
self.assertGreater(initial['tableClient'],0);self.assertTrue(initial['focusQuick'])
|
|||
|
|
self.capture('after-catalog-'+module+'-320')
|
|||
|
|
self.code("await page.getByRole('dialog').last().getByRole('button',{name:'Nuevo campo',exact:true}).click();return true;")
|
|||
|
|
self.settle();self.capture('after-quick-create-'+module);self.assertEqual(self.code("return await page.locator('[role=dialog][data-slot=dialog-content]').count();"),3)
|
|||
|
|
self.code("await page.keyboard.press('Escape');await page.locator('[role=dialog] input[data-quick-search]').waitFor({state:'visible'});return true;")
|
|||
|
|
self.settle();self.capture('after-quick-return-'+module)
|
|||
|
|
self.assertEqual(self.code("return await page.locator('[role=dialog][data-slot=dialog-content]').count();"),2)
|
|||
|
|
self.assertTrue(self.code("return await page.getByRole('button',{name:'Nuevo campo',exact:true}).evaluate(e=>e===document.activeElement);"))
|
|||
|
|
self.code("await page.getByRole('dialog').last().locator('input[data-quick-search]').fill('NO-EXISTE-L02');return true;")
|
|||
|
|
self.settle();self.assertEqual(self.geometry()['height'],initial['height'])
|
|||
|
|
self.assertEqual(self.geometry()['rows'],0)
|
|||
|
|
self.code("await page.keyboard.press('Escape');return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').count();"),1)
|
|||
|
|
self.assertTrue(self.code("return await page.evaluate(()=>document.activeElement.tagName==='BUTTON');"))
|
|||
|
|
|
|||
|
|
def test_05_english(self):
|
|||
|
|
self.resize(320,640);self.fixture(locale='en',kind='options',count=1)
|
|||
|
|
text=self.code("return await page.getByRole('dialog').locator('p[aria-live]').innerText();")
|
|||
|
|
self.assertEqual(text,'1–1 of 1 field')
|
|||
|
|
self.assertNotIn('Choose',text)
|
|||
|
|
self.record('english-range',text);self.capture('after-english-320')
|
|||
|
|
|
|||
|
|
def test_06_remote(self):
|
|||
|
|
self.resize(320,640);self.fixture(kind='remote')
|
|||
|
|
self.assertGreater(self.geometry()['tableClient'],0)
|
|||
|
|
self.code("await page.getByRole('dialog').getByRole('radio').first().check();await page.getByRole('button',{name:'Siguiente',exact:true}).click();return true;")
|
|||
|
|
self.assertEqual(self.geometry()['rows'],2)
|
|||
|
|
self.capture('after-remote-320');self.record('remote',self.geometry())
|
|||
|
|
self.code("await page.getByRole('button',{name:'Cancelar',exact:true}).click();return true;")
|
|||
|
|
self.assertEqual(self.code('return await page.locator(\'output[aria-label="Resultado"]\').textContent();'),'—')
|
|||
|
|
|
|||
|
|
def test_07_actual_assets_profile_and_domain(self):
|
|||
|
|
self.resize(1440,900)
|
|||
|
|
self.code("""const companyId="""+json.dumps(self.company.split('/')[-1])+""";
|
|||
|
|
const siteId='00000000-0000-7000-8000-000000000201';
|
|||
|
|
await page.route('**/api/companies/'+companyId,async r=>{const response=await r.fetch();const data=await response.json();data.company.sites=[{id:siteId,companyId,name:'L02 Sede sintética',active:true,address:null,phone:null}];await r.fulfill({response,json:data})});
|
|||
|
|
await page.route('**/api/assets/'+companyId,async r=>{const response=await r.fetch();const data=await response.json();const base=data.assets[0];data.assets=Array.from({length:12},(_,i)=>{const a=structuredClone(base);if(i)a.id='00000000-0000-7000-8000-'+String(300+i).padStart(12,'0');a.values.siteId=siteId;a.values.name='L02 Equipo '+String(i+1).padStart(2,'0');a.assigned=i===0;a.displayStatus=i===0?'in_use':'available';a.history=[{...a.history[0],id:'00000000-0000-7000-8000-'+String(500+i).padStart(12,'0'),values:structuredClone(a.values)}];return a});const values={name:'L02 Puesto',active:true,siteId,personId:null,computerAssetId:base.id,assetIds:[base.id]};data.workstations=[{id:'00000000-0000-7000-8000-000000000600',companyId,values,history:[{...base.history[0],id:'00000000-0000-7000-8000-000000000601',values}]}];await r.fulfill({response,json:data})});return true;
|
|||
|
|
""")
|
|||
|
|
self.navigate(self.company+'/workstations')
|
|||
|
|
self.code("await page.getByRole('button',{name:'Nuevo puesto',exact:true}).click();await page.getByRole('dialog').getByRole('button',{name:'Siguiente',exact:true}).click();return true;")
|
|||
|
|
label=self.code("return await page.getByRole('button',{name:'Seleccionar activos',exact:true}).evaluate(e=>({text:e.textContent,override:e.getAttribute('aria-label')}));")
|
|||
|
|
self.assertIsNone(label['override']);self.record('actual-asset-label',label)
|
|||
|
|
self.code("await page.getByRole('button',{name:'Seleccionar activos',exact:true}).click();return true;")
|
|||
|
|
self.settle()
|
|||
|
|
for w,h in SIZES:
|
|||
|
|
self.resize(w,h);g=self.geometry();self.record('actual-assets-'+str(w),g)
|
|||
|
|
if w==320:self.assertGreaterEqual(g['full'],2)
|
|||
|
|
self.assertGreater(g['tableClient'],0);self.assertTrue(g['footerVisible']);self.assertFalse(g['overflow'])
|
|||
|
|
if w==320:self.capture('after-actual-assets-320')
|
|||
|
|
self.code("await page.getByRole('dialog').last().locator('[role=checkbox]:not([aria-disabled=true])').first().check();await page.getByRole('dialog').last().getByRole('button',{name:'Cancelar',exact:true}).click();return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').count();"),1)
|
|||
|
|
self.code("await page.getByRole('button',{name:'Seleccionar activos',exact:true}).click();return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').last().locator('[role=checkbox][aria-checked=true]').count();"),0)
|
|||
|
|
self.code("await page.getByRole('dialog').last().locator('[role=checkbox]:not([aria-disabled=true])').first().check();await page.getByRole('dialog').last().locator('[data-slot=dialog-footer] button').last().click();return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').count();"),1)
|
|||
|
|
self.code("await page.keyboard.press('Escape');return true;")
|
|||
|
|
self.resize(1440,900);self.navigate(self.company+'/work-profiles')
|
|||
|
|
self.code("await page.getByRole('button',{name:'Nuevo perfil',exact:true}).click();await page.getByRole('dialog').getByRole('button',{name:'Seleccionar puesto',exact:true}).click();return true;")
|
|||
|
|
self.settle();text=self.code("return await page.getByRole('dialog').last().locator('p[aria-live]').innerText();")
|
|||
|
|
self.assertEqual(text,'1–1 de 1 puesto');self.record('actual-workstation-range',text);self.capture('after-actual-workstation')
|
|||
|
|
self.code("await page.keyboard.press('Escape');await page.keyboard.press('Escape');return true;")
|
|||
|
|
self.navigate(self.company+'/assets/'+self.asset_id)
|
|||
|
|
self.code("await page.getByRole('button',{name:'Asociar a dominio',exact:true}).click();await page.getByRole('dialog').locator('button[id$=\"-domain\"]').click();return true;")
|
|||
|
|
self.settle();self.resize(320,640);self.capture('after-actual-domain-320')
|
|||
|
|
self.assertTrue(self.code("return await page.getByRole('dialog').last().locator('input[data-quick-search]').evaluate(e=>e===document.activeElement);"))
|
|||
|
|
self.code("await page.keyboard.press('Escape');return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').count();"),1)
|
|||
|
|
|
|||
|
|
def test_08_checkboxes_and_effective_text_zoom(self):
|
|||
|
|
self.resize(390,844);self.fixture()
|
|||
|
|
self.code("await page.getByRole('dialog').locator('tbody tr').first().locator('td').nth(1).click();return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').getByRole('checkbox').first().getAttribute('aria-checked');"),'true')
|
|||
|
|
self.code("await page.getByRole('dialog').getByRole('checkbox').first().uncheck();return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').getByRole('checkbox').first().getAttribute('aria-checked');"),'false')
|
|||
|
|
self.code("await page.getByRole('dialog').locator('[data-slot=table-container]').evaluate(e=>e.scrollTop=e.scrollHeight);return true;")
|
|||
|
|
self.assertEqual(self.code("return await page.getByRole('dialog').getByRole('checkbox').nth(9).getAttribute('aria-disabled');"),'true')
|
|||
|
|
self.code("await page.getByRole('dialog').getByRole('button',{name:'Página siguiente',exact:true}).click();return true;")
|
|||
|
|
self.assertEqual(self.geometry()['rows'],2)
|
|||
|
|
self.code("await page.evaluate(()=>document.documentElement.style.fontSize='200%');return true;")
|
|||
|
|
self.settle();g=self.geometry();self.record('text-scale-200',g)
|
|||
|
|
self.assertGreater(g['tableClient'],0);self.assertGreater(g['bodyClient'],0)
|
|||
|
|
self.assertFalse(g['overflow'])
|
|||
|
|
self.code("await page.getByRole('dialog').getByRole('checkbox').first().focus();return true;")
|
|||
|
|
self.settle()
|
|||
|
|
focused=self.code("return await page.getByRole('dialog').getByRole('checkbox').first().evaluate(e=>{const r=e.getBoundingClientRect(),b=e.closest('[data-slot=form-body]').getBoundingClientRect();return {top:r.top,bottom:r.bottom,bodyTop:b.top,bodyBottom:b.bottom}});")
|
|||
|
|
self.record('text-scale-focus',focused)
|
|||
|
|
self.assertGreaterEqual(focused['top'],focused['bodyTop'])
|
|||
|
|
self.assertLessEqual(focused['bottom'],focused['bodyBottom'])
|
|||
|
|
self.capture('after-text-scale-200')
|
|||
|
|
self.code("await page.evaluate(()=>document.documentElement.style.fontSize='');await page.keyboard.press('Escape');return true;")
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__=='__main__':
|
|||
|
|
unittest.main(verbosity=2)
|