2026-10-25 12:46:06 +00:00
""" Listado combinado, alta local y selectores con HTTP real y Playwright MCP aislados. """
import json
2026-10-27 10:39:53 +00:00
import base64
2026-10-25 12:46:06 +00:00
import runpy
def verify ( context , fixture , password ) :
api = context [ ' api ' ]
api . resource_contract = ' 2 '
api . request ( ' /auth/login ' , { ' email ' : ' setup@audit.example.test ' , ' password ' : password } )
company = fixture [ ' company ' ]
template = api . request ( ' /resource-global-applications/template ' ) [ ' specification ' ] [ ' templateId ' ]
2026-10-27 10:39:53 +00:00
definitions = { item [ ' builtinKey ' ] : item for item in api . request ( ' /resource-catalog/ ' + company ) [ ' items ' ] if item . get ( ' builtinKey ' ) }
icon_id = definitions [ ' field:icon ' ] [ ' id ' ]
image = " data:image/png;base64, " + base64 . b64encode ( ( context [ " ROOT " ] / " apps/server/data/resource-icons/windows-10.png " ) . read_bytes ( ) ) . decode ( " ascii " )
assert_icons = runpy . run_path ( str ( context [ " ROOT " ] / " scripts/resource-image-ui-checks.py " ) ) [ " assert_list_icons " ]
for resource in api . request ( ' /resources/ ' + company ) [ ' resources ' ] :
if resource [ ' values ' ] [ ' templateId ' ] == template :
values = { * * resource [ ' values ' ] , ' characteristics ' : { * * resource [ ' values ' ] [ ' characteristics ' ] , icon_id : image } }
api . request ( ' /resources/ ' + company + ' / ' + resource [ ' id ' ] + ' /update ' , { ' values ' : values } )
os_template = definitions [ ' template:os ' ] [ ' id ' ]
api . request ( ' /resources/ ' + company , { ' values ' : { ' name ' : ' SO local — Icono PNG ' , ' templateId ' : os_template , ' active ' : True , ' notes ' : ' ' , ' characteristics ' : { icon_id : image } } } , expected = 201 )
2026-10-25 12:46:06 +00:00
creation_id = runpy . run_path ( str ( context [ ' ROOT ' ] / ' scripts/application-coincidences-ui-audit.py ' ) ) [ ' creation_id ' ]
for index in range ( 60 ) :
api . request ( ' /resource-global-applications ' , {
' creationId ' : creation_id ( ) ,
' values ' : { ' name ' : f ' Global Ñ — Aplicación de prueba { index + 1 : 02 } ' , ' templateId ' : template ,
2026-10-27 10:39:53 +00:00
' active ' : index % 5 != 0 , ' notes ' : ' Nota administrativa que no debe aparecer ' , ' characteristics ' : { icon_id : image } } } , expected = 201 )
2026-10-25 12:46:06 +00:00
api . request ( ' /auth/logout ' , { } )
tool , browser , capture = context [ ' tool ' ] , context [ ' browser ' ] , context [ ' capture ' ]
origin , directory = context [ ' origin ' ] , context [ ' directory ' ]
url = origin + f ' /companies/ { company } /resources?template= { template } '
screenshots , checks = [ ] , [ ]
def code ( client , source ) :
print ( ' Comprobación MCP de compañía: ' + source [ : 90 ] , flush = True )
try :
return tool ( client , ' browser_run_code_unsafe ' , { ' code ' : ' async (page) => { page.setDefaultTimeout(20000); page.setDefaultNavigationTimeout(20000); ' + source + ' } ' } )
except RuntimeError :
capture ( client , directory , ' company-applications-failure ' )
raise
with browser ( context [ ' config ' ] ) as client :
tool ( client , ' browser_resize ' , { ' width ' : 1440 , ' height ' : 1000 } )
tool ( client , ' browser_navigate ' , { ' url ' : origin + ' /login ' } )
code ( client , " await page.getByRole( ' button ' , { name: ' Entrar ' ,exact:true}).waitFor(); " )
tool ( client , ' browser_fill_form ' , { ' fields ' : [
{ ' name ' : ' Correo electrónico ' , ' type ' : ' textbox ' , ' target ' : ' input[type=email] ' , ' value ' : fixture [ ' email ' ] } ,
{ ' name ' : ' Contraseña ' , ' type ' : ' textbox ' , ' target ' : ' input[type=password] ' , ' value ' : fixture [ ' password ' ] } ] } )
tool ( client , ' browser_click ' , { ' target ' : ' button[type=submit] ' } )
code ( client , " await page.locator( ' [data-slot=workspace] ' ).waitFor( {timeout:30000} ); " )
tool ( client , ' browser_navigate ' , { ' url ' : url } )
code ( client , " await page.getByRole( ' button ' , { name: ' Añadir aplicación de compañía ' ,exact:true}).waitFor(); await page.getByRole( ' link ' , { name: ' Aplicación particular Ágil 1 ' ,exact:true}).waitFor(); if(await page.getByText( ' Nota administrativa que no debe aparecer ' , { exact:true}).count())throw Error( ' Se han expuesto notas globales ' ); " )
2026-10-27 10:39:53 +00:00
assert_icons ( client , code )
checks . append ( ' company-application-png-icons ' )
2026-10-25 12:46:06 +00:00
screenshots . append ( capture ( client , directory , ' company-applications-1440 ' ) )
checks . extend ( [ ' ordinary-user ' , ' company-applications-tab ' , ' public-metadata-only ' ] )
code ( client , " await page.getByRole( ' button ' , { name: ' Siguiente ' ,exact:true}).click(); await page.waitForURL(/page=2/); await page.getByRole( ' button ' , { name: ' Anterior ' ,exact:true}).click(); await page.waitForURL(/page=1/); " )
checks . append ( ' cursor-navigation ' )
code ( client , " await page.getByRole( ' textbox ' , { name: ' Buscar por nombre o proveedor ' ,exact:true}).fill( ' Global Ñ ' ); await page.getByRole( ' link ' , { name: ' Global Ñ — Aplicación de prueba 01 ' ,exact:true}).waitFor(); await page.getByText( ' Global ' , { exact:true}).first().waitFor(); " )
screenshots . append ( capture ( client , directory , ' company-applications-global ' ) )
checks . extend ( [ ' global-origin ' , ' global-navigation ' , ' remote-search ' ] )
code ( client , " await page.getByRole( ' textbox ' , { name: ' Buscar por nombre o proveedor ' ,exact:true}).fill( ' zzzx-inexistente ' ); await page.getByText( ' No hay coincidencias. Cambia la búsqueda o vuelve al inicio. ' , { exact:true}).waitFor(); " )
checks . append ( ' empty-search ' )
tool ( client , ' browser_navigate ' , { ' url ' : url } )
code ( client , " await page.getByRole( ' button ' , { name: ' Añadir aplicación de compañía ' ,exact:true}).click(); await page.getByRole( ' dialog ' , { name: ' Nuevo recurso ' ,exact:true}).waitFor(); await page.getByRole( ' button ' , { name: ' Siguiente ' ,exact:true}).click(); await page.getByRole( ' button ' , { name: ' Siguiente ' ,exact:true}).click(); await page.getByRole( ' textbox ' , { name: ' Nombre ' ,exact:true}).fill( ' Aplicación exclusiva creada desde compañía ' ); await page.getByRole( ' button ' , { name: ' Guardar ' ,exact:true}).click(); await page.getByRole( ' dialog ' , { name: ' Nuevo recurso ' ,exact:true}).waitFor( { state: ' hidden ' }); " )
checks . append ( ' local-create-dialog ' )
code ( client , " const path= " + json . dumps ( origin + ' /api/resource-application-candidates/ ' ) + " ; const list=await (await page.request.get(path+ ' " + company + " /catalog?search=exclusiva ' )).json(); if(list.items.length!==1 || list.items[0].scope!== ' company ' ||list.items[0].companyId!== ' " + company + " ' )throw Error( ' Alta fuera de compañía ' ); const foreign=await (await page.request.get(path+ ' " + fixture [ ' otherCompanies ' ] [ 0 ] + " /catalog?search=exclusiva ' )).json(); if(foreign.items.length)throw Error( ' Contaminación entre compañías ' ); " )
checks . append ( ' local-creation-isolated ' )
tool ( client , ' browser_navigate ' , { ' url ' : url } )
code ( client , " await page.getByRole( ' link ' , { name: ' Aplicación exclusiva creada desde compañía ' ,exact:true}).waitFor(); " )
for width in [ 1280 , 768 , 390 ] :
tool ( client , ' browser_resize ' , { ' width ' : width , ' height ' : 844 } )
code ( client , " await page.getByRole( ' button ' , { name: ' Añadir aplicación de compañía ' ,exact:true}).waitFor(); if(await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth+1))throw Error( ' Desbordamiento horizontal de página ' ); " )
screenshots . append ( capture ( client , directory , f ' company-applications- { width } ' ) )
checks . append ( ' responsive-four-widths ' )
tool ( client , ' browser_resize ' , { ' width ' : 1440 , ' height ' : 1000 } )
code ( client , " await page.route( ' **/api/resource-application-candidates/*/catalog?** ' ,r=>r.fulfill( { status:500,contentType: ' application/json ' ,json: { code: ' INTERNAL_ERROR ' }})); " )
tool ( client , ' browser_navigate ' , { ' url ' : url + ' &q=fallo ' } )
code ( client , " await page.getByRole( ' button ' , { name: ' Reintentar ' ,exact:true}).waitFor(); await page.unroute( ' **/api/resource-application-candidates/*/catalog?** ' ); await page.getByRole( ' button ' , { name: ' Reintentar ' ,exact:true}).click(); " )
checks . extend ( [ ' read-error ' , ' retry ' ] )
tool ( client , ' browser_navigate ' , { ' url ' : origin + f ' /companies/ { company } /assets/ { fixture [ " softwareAssets " ] [ 0 ] } ' } )
code ( client , " await page.getByRole( ' button ' , { name: ' Añadir instalación ' ,exact:true}).click(); await page.getByRole( ' button ' , { name: ' Software ' ,exact:true}).click(); await page.getByRole( ' dialog ' , { name: ' Seleccionar software ' ,exact:true}).waitFor(); await page.getByRole( ' textbox ' , { name: ' Buscar por nombre o proveedor ' ,exact:true}).fill( ' Global Ñ ' ); await page.getByRole( ' row ' ).filter( { hasText: ' Global Ñ ' }).first().waitFor(); await page.keyboard.press( ' Escape ' ); await page.keyboard.press( ' Escape ' ); " )
checks . append ( ' software-selector-global ' )
tool ( client , ' browser_navigate ' , { ' url ' : origin + f ' /companies/ { company } /work-profiles/ { fixture [ " configurationProfiles " ] [ 0 ] } /application-configurations ' } )
code ( client , " await page.getByRole( ' button ' , { name: ' Configurar aplicación ' ,exact:true}).click(); await page.getByRole( ' button ' , { name: ' Aplicación ' ,exact:true}).click(); await page.getByRole( ' dialog ' , { name: ' Seleccionar aplicación ' ,exact:true}).waitFor(); await page.getByRole( ' textbox ' , { name: ' Buscar por nombre o proveedor ' ,exact:true}).fill( ' exclusiva ' ); await page.getByRole( ' row ' ).filter( { hasText: ' Aplicación exclusiva creada desde compañía ' }).waitFor(); await page.keyboard.press( ' Escape ' ); if((await page.evaluate(()=>document.activeElement?.getAttribute( ' aria-label ' )))!== ' Aplicación ' )throw Error( ' Foco perdido ' ); await page.keyboard.press( ' Escape ' ); " )
checks . extend ( [ ' profile-selector-local ' , ' keyboard-escape ' , ' focus-return ' ] )
2026-10-27 10:39:53 +00:00
tool ( client , ' browser_navigate ' , { ' url ' : origin + ' /catalogs/global-applications ' } )
code ( client , " await page.getByRole( ' link ' ).filter( { hasText: ' Global Ñ ' }).first().waitFor(); " )
assert_icons ( client , code )
screenshots . append ( capture ( client , directory , ' global-application-icons ' ) )
tool ( client , ' browser_navigate ' , { ' url ' : origin + f ' /companies/ { company } /resources?template= { os_template } ' } )
code ( client , " await page.getByRole( ' link ' , { name: ' SO local — Icono PNG ' ,exact:true}).waitFor(); " )
assert_icons ( client , code )
screenshots . append ( capture ( client , directory , ' local-operating-system-icon ' ) )
checks . extend ( [ ' global-application-png-icons ' , ' local-operating-system-png-icon ' ] )
2026-10-25 12:46:06 +00:00
report = { ' checks ' : checks , ' screenshots ' : screenshots , ' companies ' : 15 ,
' principal ' : { ' people ' : 50 , ' assets ' : 51 , ' profiles ' : 50 , ' workstations ' : 50 , ' localApplications ' : 51 } ,
' globalApplications ' : 60 , ' database ' : ' metis_desk_test ' }
( directory / ' company-applications-report.json ' ) . write_text ( json . dumps ( report , ensure_ascii = False , indent = 2 ) + ' \n ' )
print ( ' Listado y selectores de compañía verificados. Evidencia: ' + str ( directory . relative_to ( context [ ' ROOT ' ] ) ) , flush = True )