199 lines
36 KiB
Python
199 lines
36 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""L04.3 mediante MCP existente: relaciones y selección, solo datos sintéticos GET."""
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
from pathlib import Path
|
||
|
|
import runpy
|
||
|
|
import subprocess
|
||
|
|
import unittest
|
||
|
|
from urllib.parse import quote
|
||
|
|
|
||
|
|
ROOT=Path(__file__).resolve().parent.parent
|
||
|
|
CONTEXT_SIZES=[(1920,1080),(1440,900),(1366,768),(1024,768),(768,1024),(390,844),(320,640)]
|
||
|
|
ADOPTION=runpy.run_path(str(ROOT/'scripts/catalog-configuration-ui.test.py'))
|
||
|
|
|
||
|
|
|
||
|
|
class EmbeddedRelationsUiTests(ADOPTION['CatalogConfigurationUiTests']):
|
||
|
|
artifact_directory=ROOT/'.artifacts/playwright/l04-3/after'
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def code(cls, body):
|
||
|
|
try:
|
||
|
|
return super().code("page.setDefaultTimeout(12000);page.setDefaultNavigationTimeout(25000);"+body)
|
||
|
|
except RuntimeError:
|
||
|
|
detail=super().code("return page.evaluate(()=>({path:location.pathname,focus:document.activeElement?.outerHTML.slice(0,500),list:history.state?.metisListReturn,detail:history.state?.metisDetailReturn,entry:history.state?.metisEntryState,dialogs:[...document.querySelectorAll('[role=dialog]')].map(e=>({name:e.getAttribute('aria-labelledby'),visible:e.getClientRects().length>0}))}));")
|
||
|
|
(cls.output/('return-diagnostic-'+str(os.getpid())+'.json')).write_text(json.dumps(detail,ensure_ascii=False,indent=2))
|
||
|
|
raise
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def prepare_configurations(cls):
|
||
|
|
cls.code("const f=page.adoption,r=f.snapshots['/resources/'+f.detail.company.id].resources.find(x=>x.specification.templateName==='Certificado digital');if(!r)throw Error('Falta certificado de prueba');r.values.active=true;r.history.at(-1).values.active=true;return true;")
|
||
|
|
super().prepare_configurations()
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def setUpClass(cls):
|
||
|
|
super().setUpClass()
|
||
|
|
cls.code("page.setDefaultTimeout(15000);page.setDefaultNavigationTimeout(25000);return true;")
|
||
|
|
cls.code("""const f=page.adoption,c=f.detail.company,p=f.profiles.profiles,station=f.inventory.workstations.find(w=>w.values.active),person=c.people.find(x=>x.active);let n=0;const id=()=> '019a4300-0000-7000-a000-'+String(++n).padStart(12,'0');
|
||
|
|
c.departments=Array.from({length:50},(_,i)=>({id:id(),companyId:c.id,name:'L043-AREA-'+String(i+1).padStart(2,'0')+' · Administración '+(i===1?'Pena':'Peña'),active:true}));
|
||
|
|
c.assignments=c.departments.map((d,i)=>{const values={position:'Técnico',startDate:'2026-03-01',endDate:null,isPrimary:i===0};return{id:id(),companyId:c.id,personId:person.id,departmentId:d.id,...values,history:[{id:id(),recordedAt:'2026-03-01T10:00:00.000Z',...values}]}});
|
||
|
|
const computer=station.values.computerAssetId,second=f.inventory.workstations.find(w=>w.values.active&&w.values.computerAssetId!==computer).values.computerAssetId,seen=new Set();for(const profile of p){const host=profile.values.active&&seen.has(profile.values.personId)?second:computer;if(profile.values.active)seen.add(profile.values.personId);profile.values.assetId=host;for(const revision of profile.history)revision.values.assetId=host}
|
||
|
|
for(const w of f.inventory.workstations.filter(w=>w.values.active).slice(0,5)){w.values.personId=person.id;w.history.at(-1).values.personId=person.id}
|
||
|
|
page.embedded={asset:computer,person:person.id,station:station.id};return true;""")
|
||
|
|
cls.code("await page.route(page.adoptionOrigin+'/api/work-profiles/'+page.adoptionCompany,r=>r.request().method()==='GET'?r.fulfill({json:{...page.adoption.profiles,profiles:page.adoption.profiles.profiles.filter(p=>p.id!==page.adoptionRemoved)}}):r.fallback());return true;")
|
||
|
|
cls.code((ROOT/"scripts/software-reference-read.fixture.js").read_text().replace("export async function", "async function")+"\nreturn interceptSoftwareReads(page,page.adoption.snapshots['/resources/'+page.adoptionCompany].resources,page.adoptionCompany,page.adoptionOrigin);")
|
||
|
|
cls.code((ROOT/"scripts/embedded-relations-fixture.js").read_text().replace("export async function", "async function", 1)+"\nreturn prepareEmbeddedRelations(page);")
|
||
|
|
fixture=cls.output/('embedded-fixtures-'+str(os.getpid())+'.json')
|
||
|
|
fixture.write_text(json.dumps(cls.code('return {...page.adoption,typed:{publications:page.embeddedData.publications,memberships:page.embeddedData.memberships}};'),ensure_ascii=False)+'\n')
|
||
|
|
validation="""import{readFile}from'node:fs/promises';import{DirectoryMembershipRelationshipPageSchema}from'./modules/assets/src/common/directory-membership-relationship.ts';import{NetworkSharePublicationSchema}from'./modules/assets/src/common/network-share-publications.ts';import{InventoryResponseSchema}from'./modules/assets/src/common/inventory.ts';import{CompanyResponseSchema}from'./modules/companies/src/common/dto/response/company.response.dto.ts';import{InventoryResponseSchema as ResourcesSchema}from'./modules/resources/src/common/inventory.ts';import{WorkProfilesResponseSchema}from'./modules/companies/src/common/work-profiles.ts';const f=JSON.parse(await readFile(process.argv[1],'utf8'));InventoryResponseSchema.parse(f.inventory);CompanyResponseSchema.parse(f.detail);WorkProfilesResponseSchema.parse(f.profiles);ResourcesSchema.parse(f.snapshots['/resources/'+f.detail.company.id]);NetworkSharePublicationSchema.array().parse(f.typed.publications);DirectoryMembershipRelationshipPageSchema.parse({items:f.typed.memberships,nextCursor:null});const assets=new Set(f.inventory.assets.map(x=>x.id)),resources=new Set(f.snapshots['/resources/'+f.detail.company.id].resources.map(x=>x.id));for(const p of f.typed.publications)if(!assets.has(p.assetId)||!resources.has(p.resourceId)||p.companyId!==f.detail.company.id)throw Error('Publicación fuera del grafo');for(const m of f.typed.memberships)if(!assets.has(m.source.id)||!resources.has(m.target.id))throw Error('Pertenencia fuera del grafo');const pairs=new Set();for(const p of f.profiles.profiles.filter(p=>p.values.active)){const key=p.companyId+':'+p.values.personId+':'+p.values.assetId;if(pairs.has(key))throw Error('Perfil activo duplicado para la misma persona y ordenador');pairs.add(key)}console.log('Grafo de relaciones: schemas y cardinalidades correctos.');"""
|
||
|
|
r=subprocess.run(['node','--import','tsx','--input-type=module','-e',validation,str(fixture)],cwd=ROOT,capture_output=True,text=True,timeout=60)
|
||
|
|
(cls.output/('embedded-validation-'+str(os.getpid())+'.log')).write_text(r.stdout+r.stderr)
|
||
|
|
if r.returncode:raise RuntimeError('Fixture de relaciones inválida; revisar evidencia.')
|
||
|
|
|
||
|
|
def test_81_remote_current_eligibility(self):
|
||
|
|
self.resize(390,844)
|
||
|
|
module='/@fs/'+quote(str(ROOT/'packages/rdx-ui/src/picker-browser-fixture.tsx'),safe='/')
|
||
|
|
self.code('await page.adoptionGoto('+json.dumps(self.origin+self.base+'/assets')+");await page.locator('main table').waitFor();await page.evaluate(async({module})=>{const f=await import(module),host=document.createElement('div');document.body.append(host);window.embeddedFixture=f.mountPickerFixture(host,{count:30,locale:'es',longNames:false,kind:'remote'})},{module:"+json.dumps(module)+"});await page.getByRole('button',{name:'Seleccionar campos',exact:true}).click();await page.getByRole('dialog').getByRole('radio').first().click();await page.getByRole('button',{name:'Invalidar primera opción',exact:true}).click();return true;")
|
||
|
|
actual=self.code("return {disabled:await page.getByRole('button',{name:'Seleccionar',exact:true}).isDisabled(),radioDisabled:await page.getByRole('dialog').getByRole('radio').first().isDisabled()};")
|
||
|
|
self.record('remote-eligibility',actual);self.capture('remote-eligibility-390');self.assertTrue(actual['radioDisabled']);self.assertTrue(actual['disabled'])
|
||
|
|
|
||
|
|
def test_82_embedded_tables_return(self):
|
||
|
|
self.resize(1440,900);ids=self.code('return page.embedded;')
|
||
|
|
self.code('await page.adoptionGoto('+json.dumps(self.origin+self.base+'/assets/'+ids['asset']+'?q=consulta-del-padre&sort=name&direction=desc&page=3')+");await page.locator('main table[aria-label=\"Perfiles de usuario\"]').waitFor();return true;")
|
||
|
|
self.code("const r=page.locator('main table[aria-label=\"Perfiles de usuario\"]').locator('..').locator('..');await r.locator('input[data-quick-search]').fill(' ADMINISTRACION ');await r.getByRole('columnheader').filter({hasText:'Nombre del perfil'}).getByRole('button').click();await r.getByRole('button',{name:'Página 3',exact:true}).click();await r.locator('tbody a').first().focus();await r.evaluate(root=>{root.querySelector('tbody a').scrollIntoView({block:'center'});root.querySelector('[data-slot=table-container]').scrollLeft=40});return true;")
|
||
|
|
def snapshot():return self.code("return page.locator('main table[aria-label=\"Perfiles de usuario\"]').evaluate(table=>{const root=table.closest('[data-slot=entity-table]'),v=root.closest('[data-slot=workspace-scroll]');return {query:root.querySelector('input[data-quick-search]').value,page:root.querySelector('[aria-current=page]')?.textContent,sort:table.querySelector('th[aria-sort]')?.getAttribute('aria-sort'),rows:[...table.querySelectorAll('tbody tr[data-entity-id]')].map(x=>x.dataset.entityId),y:v.scrollTop,x:root.querySelector('[data-slot=table-container]').scrollLeft,focus:document.activeElement.getAttribute('href'),top:root.getBoundingClientRect().top+v.scrollTop,rootHeight:root.offsetHeight,saved:history.state?.metisDetailReturn?.y,statuses:[...document.querySelectorAll('main [role=status],main [role=alert]')].filter(e=>e.getClientRects().length).map(e=>e.textContent)}});")
|
||
|
|
self.code("await page.getByRole('table',{name:'Pertenencias a dominios',exact:true}).locator('tbody tr[data-entity-id]').first().waitFor();await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).locator('tbody tr[data-entity-id]').first().waitFor();await page.locator('main table[aria-label=\"Perfiles de usuario\"] tbody a').first().scrollIntoViewIfNeeded();return true;")
|
||
|
|
self.settle();before=snapshot();self.record('embedded-before',before)
|
||
|
|
self.code("await page.locator('main table[aria-label=\"Perfiles de usuario\"] tbody a').first().click();await page.getByRole('heading',{name:/Entorno actual/}).waitFor();await page.goBack();await page.locator('main table[aria-label=\"Perfiles de usuario\"]').waitFor();return true;");self.wait_return_focus(before['rows'][0]);self.settle();after=snapshot();self.record('embedded-after',after);self.capture('embedded-return-1440')
|
||
|
|
for key in ['query','page','sort','rows','y','x','focus']:self.assertEqual(before[key],after[key],key)
|
||
|
|
|
||
|
|
def detail(self, section='assets', entity=None):
|
||
|
|
ids=self.code('return {...page.embedded,domain:page.embeddedData.domain};')
|
||
|
|
entity=entity or ids['asset']
|
||
|
|
self.code('await page.adoptionGoto('+json.dumps(self.origin+self.base+'/'+section+'/'+entity)+");await page.locator('main h1,main h2').first().waitFor();return true;")
|
||
|
|
return ids
|
||
|
|
|
||
|
|
def open_publication_picker(self):
|
||
|
|
self.detail()
|
||
|
|
self.code("await page.getByRole('button',{name:'Asignar publicación',exact:true}).click();await page.getByRole('button',{name:'Seleccionar carpeta compartida',exact:true}).click();await page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true}).getByRole('radio').first().waitFor();return true;")
|
||
|
|
|
||
|
|
def test_83_remote_cursor_draft_and_sizes(self):
|
||
|
|
for width,height in CONTEXT_SIZES:
|
||
|
|
self.resize(width,height);self.open_publication_picker()
|
||
|
|
before=self.code("const d=page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true});await d.getByRole('radio').first().click();return {id:await d.locator('tbody tr').first().getAttribute('data-entity-id'),label:await d.locator('tbody tr').first().innerText()};")
|
||
|
|
self.code("const d=page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true});await d.getByRole('button',{name:'Siguiente',exact:true}).click();await d.locator('tbody').getByText(/L043-SHARE-21/).waitFor();await d.locator('input[data-quick-search]').fill('L043-SHARE-49');await d.locator('tbody').getByText(/L043-SHARE-49/).waitFor();return true;")
|
||
|
|
measured=self.code("const d=page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true});return d.evaluate(e=>({width:innerWidth,overflow:document.documentElement.scrollWidth>innerWidth,height:e.getBoundingClientRect().height,top:e.getBoundingClientRect().top,bottom:e.getBoundingClientRect().bottom,viewport:innerHeight,checked:e.querySelectorAll('[role=radio][aria-checked=true]').length,confirmDisabled:[...e.querySelectorAll('button')].find(b=>b.textContent.trim()==='Seleccionar').disabled,headers:[...e.querySelectorAll('th')].map(t=>t.textContent)}));")
|
||
|
|
self.record('remote-capacity-'+str(width),measured);self.assertFalse(measured['overflow']);self.assertLessEqual(measured['bottom'],height+1);self.assertGreaterEqual(measured['top'],-1);self.assertEqual(measured['checked'],0);self.assertFalse(measured['confirmDisabled'])
|
||
|
|
if width in [1920,1024,390,320]:self.capture('publication-picker-'+str(width))
|
||
|
|
# Confirmar solo aplica al editor padre. Cancelar este último no escribe nada.
|
||
|
|
self.code("await page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true}).getByRole('button',{name:'Seleccionar',exact:true}).click();await page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true}).waitFor({state:'hidden'});return {before: null,dialogs:await page.getByRole('dialog').allTextContents(),buttons:await page.getByRole('dialog').getByRole('button').allTextContents()};")
|
||
|
|
self.record('applied-parent-'+str(width),self.code("return {dialogs:await page.getByRole('dialog').allTextContents(),buttons:await page.getByRole('dialog').getByRole('button').allTextContents()};"));self.assertIn('L043-SHARE-01',self.code("return page.getByRole('dialog').innerText();"))
|
||
|
|
self.code("await page.keyboard.press('Escape');await page.getByRole('dialog').waitFor({state:'hidden'});return true;")
|
||
|
|
self.open_publication_picker();state=self.code("const d=page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true});return {query:await d.locator('input[data-quick-search]').inputValue(),selected:await d.getByRole('radio',{checked:true}).count(),confirmDisabled:await d.getByRole('button',{name:'Seleccionar',exact:true}).isDisabled()};")
|
||
|
|
self.assertEqual(state,{'query':'','selected':0,'confirmDisabled':True});self.record('remote-reopen-'+str(width),state)
|
||
|
|
self.code("await page.keyboard.press('Escape');await page.waitForFunction(()=>document.activeElement.textContent.trim()==='Seleccionar carpeta compartida');await page.keyboard.press('Escape');return true;")
|
||
|
|
self.assertEqual(self.code('return page.blockedWrites;'),[])
|
||
|
|
|
||
|
|
def test_84_remote_obsolete_retry_and_empty(self):
|
||
|
|
self.resize(390,844);self.open_publication_picker()
|
||
|
|
self.code("await page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true}).locator('input[data-quick-search]').fill('ANTIGUA');return true;")
|
||
|
|
self.code("if(!page.embeddedData.held)throw Error('No se inició la consulta antigua');await page.getByRole('dialog',{name:'Seleccionar carpeta compartida',exact:true}).locator('input[data-quick-search]').fill('L043-SHARE-50');await page.getByRole('dialog').last().locator('tbody').getByText(/L043-SHARE-50/).waitFor();page.embeddedData.release();return true;")
|
||
|
|
self.settle();old=self.code("return {query:await page.getByRole('dialog').last().locator('input[data-quick-search]').inputValue(),old:await page.getByText('RESPUESTA ANTIGUA',{exact:true}).count(),rows:await page.getByRole('dialog').last().locator('tbody').innerText()};");self.record('obsolete-response',old);self.assertEqual(old['old'],0);self.assertIn('50',old['rows'])
|
||
|
|
self.code("page.embeddedData.fault='options';await page.getByRole('dialog').last().locator('input[data-quick-search]').fill('FALLO');await page.getByRole('dialog').last().getByRole('alert').waitFor();return true;")
|
||
|
|
self.capture('remote-error-390');self.assertTrue(self.code("return page.getByRole('dialog').last().getByRole('button',{name:'Seleccionar',exact:true}).isDisabled();"))
|
||
|
|
self.code("page.embeddedData.fault=null;await page.getByRole('dialog').last().getByRole('button',{name:'Reintentar',exact:true}).click();await page.getByRole('dialog').last().getByRole('alert').waitFor({state:'hidden'});await page.waitForFunction(()=>document.activeElement.hasAttribute('data-quick-search'));return true;")
|
||
|
|
self.record('retry-empty',self.code("return {focus:await page.evaluate(()=>document.activeElement.tagName),empty:await page.getByRole('dialog').last().innerText()};"));self.assertIn('No hay elementos',self.code("return page.getByRole('dialog').last().innerText();"));self.code("await page.keyboard.press('Escape');await page.keyboard.press('Escape');return true;")
|
||
|
|
|
||
|
|
def test_85_publication_cursor_return_and_states(self):
|
||
|
|
self.resize(1440,900);self.detail()
|
||
|
|
self.code("const table=page.getByRole('table',{name:'Recursos compartidos publicados',exact:true});await table.locator('tbody tr[data-entity-id]').first().waitFor();const root=table.locator('..').locator('..').locator('..');await root.getByRole('button',{name:'Siguiente',exact:true}).click();await table.getByText(/L043-SHARE-21/).waitFor();await root.getByRole('button',{name:'Siguiente',exact:true}).click();await table.getByText(/L043-SHARE-41/).waitFor();await table.locator('tbody a').first().focus();await table.locator('tbody a').first().scrollIntoViewIfNeeded();return true;")
|
||
|
|
def rows():return self.code("return page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).evaluate(t=>({ids:[...t.querySelectorAll('tbody tr[data-entity-id]')].map(e=>e.dataset.entityId),y:t.closest('[data-slot=workspace-scroll]').scrollTop,href:document.activeElement.getAttribute('href')}));")
|
||
|
|
self.settle();before=rows();self.code("await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).locator('tbody a').first().click();await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).waitFor();await page.goBack();await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).waitFor();return true;");self.wait_return_focus(before['ids'][0]);self.settle();after=rows();self.record('publication-cursor-return',{'before':before,'after':after});self.assertEqual(before,after)
|
||
|
|
self.code("page.embeddedData.fault='publications';await page.evaluate(()=>{window.dispatchEvent(new Event('offline'));window.dispatchEvent(new Event('online'))});await page.getByText('No se pudo actualizar la información. Se muestran los últimos datos disponibles.',{exact:true}).first().waitFor();return true;")
|
||
|
|
self.assertEqual(before['ids'],rows()['ids']);self.capture('publication-stale-1440')
|
||
|
|
self.code("page.embeddedData.fault=null;const table=page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}),root=table.locator('..').locator('..').locator('..');await root.getByRole('button',{name:'Reintentar',exact:true}).click();await page.getByText('No se pudo actualizar la información. Se muestran los últimos datos disponibles.',{exact:true}).waitFor({state:'hidden'});return true;")
|
||
|
|
self.assertEqual(before['ids'],rows()['ids']);self.record('publication-retry-same-cursor',rows())
|
||
|
|
|
||
|
|
def test_86_domain_resource_cursor_and_asset_link(self):
|
||
|
|
self.resize(320,640);ids=self.code('return {domain:page.embeddedData.domain};');self.detail('resources',ids['domain'])
|
||
|
|
self.code("const t=page.getByRole('table',{name:'Pertenencias a dominios',exact:true});await t.locator('tbody tr[data-entity-id]').first().waitFor();const root=t.locator('..').locator('..');await root.getByRole('button',{name:'Siguiente',exact:true}).click();await t.locator('tbody tr[data-entity-id]').first().waitFor();return true;")
|
||
|
|
state=self.code("return page.getByRole('table',{name:'Pertenencias a dominios',exact:true}).evaluate(t=>({rows:[...t.querySelectorAll('tbody tr[data-entity-id]')].map(e=>e.dataset.entityId),links:[...t.querySelectorAll('tbody a')].map(e=>e.getAttribute('href')),overflow:document.documentElement.scrollWidth>innerWidth,horizontal:t.parentElement.scrollWidth>t.parentElement.clientWidth}));")
|
||
|
|
self.record('domain-cursor-320',state);self.capture('domain-cursor-320');self.assertEqual(len(state['rows']),20);self.assertFalse(state['overflow']);self.assertEqual(len(set(state['rows'])),20);self.assertTrue(all('/assets/'in href for href in state['links']))
|
||
|
|
self.assertTrue(self.code("return page.getByText('Esta página no contiene controladores vigentes.',{exact:true}).isVisible();"))
|
||
|
|
self.code("const t=page.getByRole('table',{name:'Pertenencias a dominios',exact:true});await t.locator('tbody a').first().focus();await t.locator('tbody a').first().scrollIntoViewIfNeeded();await t.locator('..').evaluate(e=>e.scrollLeft=40);return true;")
|
||
|
|
def origin():return self.code("return page.getByRole('table',{name:'Pertenencias a dominios',exact:true}).evaluate(t=>({ids:[...t.querySelectorAll('tbody tr[data-entity-id]')].map(e=>e.dataset.entityId),x:t.parentElement.scrollLeft,y:t.closest('[data-slot=form-body]')?.scrollTop,focus:document.activeElement.getAttribute('href')}));")
|
||
|
|
self.settle();before=origin();self.code("await page.keyboard.press('Enter');await page.locator('main h1').waitFor();await page.goBack();await page.getByRole('table',{name:'Pertenencias a dominios',exact:true}).waitFor();return true;");self.wait_return_focus(before['ids'][0]);self.settle();after=origin();self.record('resource-relationship-modal-return',{'before':before,'after':after});self.assertEqual(before,after)
|
||
|
|
|
||
|
|
|
||
|
|
def test_87_independent_tables_and_removed_origin(self):
|
||
|
|
self.resize(390,844);self.detail()
|
||
|
|
self.code("const a=page.getByRole('table',{name:'Perfiles de usuario',exact:true}).locator('..').locator('..'),b=page.getByRole('table',{name:'Software del ordenador',exact:true}).locator('..').locator('..');await a.locator('input[data-quick-search]').fill('ADMINISTRACION');await a.getByRole('button',{name:'Página 3',exact:true}).click();await b.locator('input[data-quick-search]').fill('2026');await b.getByRole('button',{name:'Página 2',exact:true}).click();await b.locator('tbody a').first().focus();await b.locator('tbody a').first().scrollIntoViewIfNeeded();await b.locator('[data-slot=table-container]').evaluate(e=>e.scrollLeft=120);return true;")
|
||
|
|
def state():return self.code("return page.locator('main [data-slot=entity-table]').evaluateAll(tables=>tables.map(t=>({title:t.getAttribute('aria-label'),query:t.querySelector('input[data-quick-search]').value,page:t.querySelector('[aria-current=page]')?.textContent,x:t.querySelector('[data-slot=table-container]').scrollLeft,ids:[...t.querySelectorAll('tbody tr[data-entity-id]')].map(e=>e.dataset.entityId)})));")
|
||
|
|
self.settle();before=state();self.code("await page.keyboard.press('Enter');await page.getByRole('heading').filter({hasText:/AUDIT/}).first().waitFor();await page.goBack();await page.getByRole('table',{name:'Software del ordenador',exact:true}).waitFor();return true;");self.wait_return_focus(before[-1]['ids'][0]);self.settle();after=state();self.record('independent-tables-return',{'before':before,'after':after});self.assertEqual(before,after);self.capture('independent-tables-390')
|
||
|
|
# El registro desaparece únicamente de la respuesta GET durante el viaje.
|
||
|
|
self.code("await page.getByRole('table',{name:'Perfiles de usuario',exact:true}).locator('tbody a').first().focus();await page.getByRole('table',{name:'Perfiles de usuario',exact:true}).locator('tbody a').first().scrollIntoViewIfNeeded();return true;")
|
||
|
|
removed=self.code("return page.getByRole('table',{name:'Perfiles de usuario',exact:true}).locator('tbody tr[data-entity-id]').first().getAttribute('data-entity-id');")
|
||
|
|
self.code("await page.getByRole('table',{name:'Perfiles de usuario',exact:true}).locator('tbody a').first().click();await page.getByRole('heading',{name:/Entorno actual/}).waitFor();page.adoptionRemoved="+json.dumps(removed)+";await page.goBack();await page.waitForFunction(()=>document.activeElement.hasAttribute('data-quick-search'));return true;")
|
||
|
|
self.settle();self.assertEqual(self.code("return page.evaluate(()=>document.activeElement.closest('[data-slot=entity-table]').getAttribute('aria-label'));"),'Perfiles de usuario');self.assertNotIn(removed,state()[0]['ids']);self.record('removed-embedded-origin',state());self.code('page.adoptionRemoved=null;return true;')
|
||
|
|
|
||
|
|
def test_88_person_assignments_and_return(self):
|
||
|
|
self.resize(1440,900);ids=self.code('return page.embedded;');self.detail('people',ids['person'])
|
||
|
|
self.code("const table=page.locator('main [data-slot=entity-table]').first();await table.locator('input[data-quick-search]').fill('ADMINISTRACION');await table.getByRole('button',{name:'Página 3',exact:true}).click();await page.locator('main [data-entity-id^=\"responsible:\"] a').first().focus();await page.locator('main [data-entity-id^=\"responsible:\"] a').first().scrollIntoViewIfNeeded();return true;")
|
||
|
|
before=self.values();origin=self.code("return page.evaluate(()=>document.activeElement.closest('[data-entity-id]').dataset.entityId);");self.code("await page.locator('main [data-entity-id^=\"responsible:\"] a').first().click();await page.locator('main h1').waitFor();await page.goBack();await page.locator('main [data-slot=entity-table]').waitFor();return true;");self.wait_return_focus(origin);self.settle();after=self.values()
|
||
|
|
for key in ['query','page','rows','y','focusHref']:self.assertEqual(before[key],after[key],key)
|
||
|
|
self.record('person-assignments-return',{'before':before,'after':after});self.capture('person-related-1440')
|
||
|
|
|
||
|
|
def test_89_workstation_preview_and_profile_expansion_return(self):
|
||
|
|
self.resize(390,844);ids=self.code('return page.embedded;');self.detail('workstations',ids['station'])
|
||
|
|
self.code("const table=page.locator('main [data-slot=compact-table]').filter({has:page.getByRole('link',{name:/L04-PERFIL/})});await table.getByRole('link').first().waitFor();const panel=table.locator('..').locator('..').locator('..');await panel.getByRole('button',{name:'Mostrar todos',exact:true}).click();return true;")
|
||
|
|
before=self.code("const table=page.locator('main [data-slot=compact-table]').filter({has:page.getByRole('link',{name:/L04-PERFIL/})});const row=table.locator('tbody tr[data-entity-id]').nth(20);await row.getByRole('link').first().focus();await row.getByRole('link').first().scrollIntoViewIfNeeded();return {id:await row.getAttribute('data-entity-id'),count:await table.locator('tbody tr[data-entity-id]').count()};")
|
||
|
|
self.assertGreater(before['count'],5);self.code("await page.locator('main tr[data-entity-id="+json.dumps(before['id'])+"]').getByRole('link').first().click();await page.getByRole('heading',{name:/Entorno actual/}).waitFor();await page.goBack();await page.locator('main h1').waitFor();return true;");self.wait_return_focus(before['id']);self.settle()
|
||
|
|
after=self.code("return {count:await page.locator('main [data-slot=compact-table] tbody tr[data-entity-id]').count(),focus:await page.evaluate(()=>document.activeElement.closest('[data-entity-id]')?.dataset.entityId),overflow:await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth)};")
|
||
|
|
self.assertEqual(before['count'],after['count']);self.assertEqual(before['id'],after['focus']);self.assertFalse(after['overflow']);self.record('station-preview-return',{'before':before,'after':after});self.capture('station-preview-return-390')
|
||
|
|
self.code("const row=page.locator('main tr[data-entity-id="+json.dumps(before['id'])+"]');await row.getByRole('button',{name:/Mostrar configuraciones/}).click();await row.getByRole('button',{name:/Ocultar configuraciones/}).waitFor();await row.getByRole('link').first().click();await page.getByRole('heading',{name:/Entorno actual/}).waitFor();await page.goBack();await page.locator('main h1').waitFor();return true;")
|
||
|
|
self.wait_return_focus(before['id']);expanded=self.code("return page.locator('main tr[data-entity-id="+json.dumps(before['id'])+"]').getByRole('button',{name:/Ocultar configuraciones/}).getAttribute('aria-expanded');");self.assertEqual(expanded,'true');self.record('station-profile-expansion-return',{'profile':before['id'],'expanded':expanded})
|
||
|
|
|
||
|
|
|
||
|
|
def test_90_publications_history_refetch_and_denied(self):
|
||
|
|
self.resize(390,844);self.detail()
|
||
|
|
self.code("await page.getByRole('button',{name:'Historial de publicadores',exact:true}).click();await page.getByRole('table',{name:'Historial de publicadores',exact:true}).locator('tbody tr[data-entity-id]').first().waitFor();return true;")
|
||
|
|
status=self.code("const a=page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}),h=page.getByRole('table',{name:'Historial de publicadores',exact:true});return {active:await a.locator('tbody tr[data-entity-id]').count(),history:await h.locator('tbody tr[data-entity-id]').count(),historicalActions:await h.getByRole('button',{name:'Finalizar publicación',exact:true}).count()};")
|
||
|
|
self.assertEqual(status['active'],20);self.assertEqual(status['history'],20);self.assertEqual(status['historicalActions'],0);self.record('publication-history',status)
|
||
|
|
self.code("page.embeddedData.fault='hold-publications';await page.evaluate(()=>{window.dispatchEvent(new Event('offline'));window.dispatchEvent(new Event('online'))});await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).and(page.locator('[aria-busy=true]')).waitFor();return true;")
|
||
|
|
current=self.code("return {rows:await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).locator('tbody tr[data-entity-id]').count(),busy:await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).getAttribute('aria-busy')};");self.assertEqual(current['rows'],20);self.assertEqual(current['busy'],'true');self.record('publication-refresh',current)
|
||
|
|
self.code("page.embeddedData.fault=null;for(const release of page.embeddedData.releases.splice(0))release();return true;")
|
||
|
|
# Dos instancias (activa e histórica) pueden estar esperando; cerrar la página cancela esas lecturas.
|
||
|
|
self.code("await page.adoptionGoto(page.adoptionOrigin+'/companies');return true;");self.detail()
|
||
|
|
self.code("page.embeddedData.fault='denied';await page.evaluate(()=>{window.dispatchEvent(new Event('offline'));window.dispatchEvent(new Event('online'))});await page.getByText('No tienes permiso para consultar esta información de la compañía.',{exact:true}).first().waitFor();return true;")
|
||
|
|
self.assertEqual(self.code("return page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).locator('tbody tr[data-entity-id]').count();"),0);self.record('publication-denied',self.code("return {rows:await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).locator('tbody tr[data-entity-id]').count()};"));self.capture('publication-denied-390');self.code("page.embeddedData.fault=null;return true;")
|
||
|
|
|
||
|
|
def test_91_resource_publisher_and_ineligible_asset(self):
|
||
|
|
self.resize(320,640);share=self.code('return page.embeddedData.shares[0].id;');self.detail('resources',share)
|
||
|
|
self.code("await page.getByRole('table',{name:'Recursos compartidos publicados',exact:true}).locator('tbody tr[data-entity-id]').first().waitFor();return true;")
|
||
|
|
self.assertTrue(self.code("return page.getByRole('button',{name:'Asignar publicación',exact:true}).isDisabled();"))
|
||
|
|
self.code("await page.getByRole('button',{name:'Cambiar publicador',exact:true}).first().click();await page.getByRole('button',{name:'Seleccionar activo publicador',exact:true}).click();await page.getByRole('dialog',{name:'Seleccionar activo publicador',exact:true}).getByRole('radio').first().waitFor();return true;")
|
||
|
|
disabled=self.code("const d=page.getByRole('dialog',{name:'Seleccionar activo publicador',exact:true});return {disabled:await d.getByRole('radio').first().isDisabled(),confirm:await d.getByRole('button',{name:'Seleccionar',exact:true}).isDisabled(),overflow:await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth)};");self.assertTrue(disabled['disabled']);self.assertTrue(disabled['confirm']);self.assertFalse(disabled['overflow']);self.record('publisher-ineligible-320',disabled);self.capture('publisher-ineligible-320')
|
||
|
|
self.code("await page.getByRole('dialog',{name:'Seleccionar activo publicador',exact:true}).getByRole('radio').nth(1).check();await page.getByRole('dialog',{name:'Seleccionar activo publicador',exact:true}).getByRole('button',{name:'Seleccionar',exact:true}).click();await page.getByRole('dialog',{name:'Cambiar publicador',exact:true}).getByRole('button',{name:'Confirmar',exact:true}).waitFor();return true;")
|
||
|
|
self.assertFalse(self.code("return page.getByRole('dialog',{name:'Cambiar publicador',exact:true}).getByRole('button',{name:'Confirmar',exact:true}).isDisabled();"));self.code("await page.keyboard.press('Escape');await page.getByRole('dialog',{name:'Cambiar publicador',exact:true}).waitFor({state:'hidden'});return true;");self.assertEqual(self.code('return page.blockedWrites;'),[])
|
||
|
|
|
||
|
|
def test_92_domain_immediate_picker_excluded_and_focus(self):
|
||
|
|
self.resize(390,844);self.detail()
|
||
|
|
self.code("await page.getByRole('button',{name:'Trasladar de dominio',exact:true}).click();await page.getByRole('dialog',{name:'Trasladar de dominio',exact:true}).locator('button[aria-haspopup=dialog]').click();await page.getByRole('dialog',{name:'Seleccionar dominio Windows',exact:true}).getByRole('button',{name:/AUDIT-DOMAIN/}).waitFor();return true;")
|
||
|
|
self.assertTrue(self.code("return page.getByRole('dialog',{name:'Seleccionar dominio Windows',exact:true}).getByRole('button',{name:/AUDIT-DOMAIN/}).isDisabled();"))
|
||
|
|
self.code("await page.getByRole('dialog',{name:'Seleccionar dominio Windows',exact:true}).locator('input[data-quick-search]').fill('ningun-dominio');await page.getByText('No hay dominios activos que coincidan con la búsqueda.',{exact:true}).waitFor();await page.keyboard.press('Escape');await page.waitForFunction(()=>document.activeElement.textContent.trim()==='Seleccionar dominio Windows',null,{timeout:12000});return true;")
|
||
|
|
self.record('domain-picker-excluded-focus',self.code("return {focus:await page.evaluate(()=>document.activeElement.textContent.trim()),dialogs:await page.getByRole('dialog').count()};"));self.code("await page.keyboard.press('Escape');await page.getByRole('dialog').waitFor({state:'hidden'});return true;");self.assertEqual(self.code('return page.blockedWrites;'),[])
|
||
|
|
|
||
|
|
def test_93_printer_usage_history_return(self):
|
||
|
|
self.resize(390,844);printer=self.code("return page.configurationData.data['/printer-configurations/'+page.adoptionCompany].configurations[0].values.printerId;");self.detail('assets',printer)
|
||
|
|
self.code("await page.getByText('Configuraciones históricas (10)',{exact:true}).click();const summary=page.getByText('Configuraciones históricas (10)',{exact:true});await summary.locator('..').locator('a').first().focus();await summary.locator('..').locator('a').first().scrollIntoViewIfNeeded();return true;")
|
||
|
|
origin=self.code("return page.evaluate(()=>document.activeElement.closest('[data-entity-id]').dataset.entityId);")
|
||
|
|
self.code("await page.keyboard.press('Enter');await page.getByRole('heading',{name:/Entorno actual/}).waitFor();await page.goBack();return true;");self.wait_return_focus(origin);self.settle();state=self.code("return {open:await page.getByText('Configuraciones históricas (10)',{exact:true}).locator('..').getAttribute('open'),focus:await page.evaluate(()=>document.activeElement.closest('[data-entity-id]')?.dataset.entityId),overflow:await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth)};")
|
||
|
|
self.assertIsNotNone(state['open']);self.assertEqual(state['focus'],origin);self.assertFalse(state['overflow']);self.record('printer-history-return',state);self.capture('printer-usage-390')
|
||
|
|
|
||
|
|
|
||
|
|
def load_tests(loader,tests,pattern):
|
||
|
|
return unittest.TestSuite(EmbeddedRelationsUiTests(n)for n in sorted(EmbeddedRelationsUiTests.__dict__)if n.startswith('test_'))
|
||
|
|
|
||
|
|
|
||
|
|
if __name__=='__main__':unittest.main(verbosity=2)
|