- Added `removeAccessAccount` function to handle account deletion in the database, ensuring no external references exist. - Created `deleteAccount` API function to facilitate account deletion requests from the client. - Developed `AccountDeleteDialog` component for user confirmation before account deletion, integrating with the delete API. - Introduced resource image handling with validation for PNG uploads, including UI components for image selection and preview. - Implemented resource icon template evolution logic to maintain icon bindings without overwriting existing fields. - Added scripts for auditing account deletion and usage checks, ensuring robust testing of the new features.
102 lines
10 KiB
Python
102 lines
10 KiB
Python
"""Listado combinado, alta local y selectores con HTTP real y Playwright MCP aislados."""
|
|
import json
|
|
import base64
|
|
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']
|
|
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)
|
|
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,
|
|
'active': index % 5 != 0, 'notes': 'Nota administrativa que no debe aparecer', 'characteristics': {icon_id: image}}}, expected=201)
|
|
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');")
|
|
assert_icons(client, code)
|
|
checks.append('company-application-png-icons')
|
|
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'])
|
|
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'])
|
|
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)
|