El proyecto automation-arqc extrae en tres clases reutilizables los patrones de interfaz Angular Material que se repiten en todos los módulos del sistema municipal de impuestos: la navegación por el menú acordeón lateral (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Giofelix/Proyecto-automatizacion-Empresa/llms.txt
Use this file to discover all available pages before exploring further.
MenuNavegacion), los botones de acción de mantenimiento CRUD (AccionesCrud) y los controles de filtrado, paginación y selección de filas de las tablas de datos (ComponentesTabla). Cualquier Page Object de módulo específico instancia estas tres clases en lugar de duplicar locators.
MenuNavegacion
Navega el acordeón lateral Angular Material con animaciones incluidas.
AccionesCrud
Abstrae los botones de iconos Material: add, save, edit, delete, Aceptar y Cancelar.
ComponentesTabla
Controla filtros, paginación y selección de filas en las tablas
mat-table.MenuNavegacion
MenuNavegacion abstrae el menú lateral de la aplicación, construido con componentes mat-expansion-panel (acordeón) de Angular Material. Gestiona automáticamente la apertura de paneles, la asignación de IDs temporales para evitar referencias obsoletas y la espera de las animaciones de expansión.
Importación
Constructor
page de Playwright y establece dos propiedades:
| Propiedad | Valor inicial | Descripción |
|---|---|---|
container | page.locator('.mat-drawer-inner-container') | Raíz del menú lateral; contexto de búsqueda inicial |
contextoActual | this.container | Referencia al nodo activo; se actualiza en cada seleccionarOpcion() |
Métodos
navegar(rutaOpciones)
Navega a través de una secuencia de elementos del menú acordeón, procesándolos en orden de izquierda a derecha (de raíz a hoja).
Array con los nombres exactos (insensible a mayúsculas) de cada nodo de la ruta, desde el nivel superior hasta el ítem hoja.
contextoActual a container para garantizar una búsqueda limpia independientemente del estado previo. Por cada nombre en el array delega a seleccionarOpcion().
seleccionarOpcion(nombreOpcion)
Localiza y hace clic sobre un elemento del menú dentro del contexto actual.
Texto exacto del ítem (se usa
new RegExp('^' + nombreOpcion + '$', 'i')).- Busca dentro de
contextoActualelementos de tipobutton,mat-panel-title,.btnMenu,.spanAcordeonospancuyo texto coincida exactamente connombreOpcion. - Llama a
scrollIntoViewIfNeeded()y verifica visibilidad (timeout 5 s). - Evalúa en el DOM si el elemento está dentro de un
mat-expansion-panel-header(acordeón):- Si es acordeón: lee o asigna un ID temporal al
mat-expansion-panelpadre, hace clic, actualizacontextoActualal.mat-expansion-panel-contentde ese panel y espera 800 ms para la animación de Angular Material. - Si es hoja (no acordeón): hace clic, restaura
contextoActualacontainery esperanetworkidlepara la carga del módulo destino.
- Si es acordeón: lee o asigna un ID temporal al
El ID temporal (
pw-nav-XXXXXXXXX) se asigna directamente en el DOM mediante evaluate() para evitar el encadenamiento de locators que provocaría errores de tipo “Received: hidden” cuando el panel se anida dentro de otro ya abierto.Ejemplo
AccionesCrud
AccionesCrud envuelve los seis botones de acción estándar que aparecen en la barra de herramientas de todos los formularios de mantenimiento: agregar, guardar, editar, eliminar y los botones de confirmación de diálogo Aceptar/Cancelar.
Importación
Constructor
| Propiedad | Locator | Icono Material | Acción |
|---|---|---|---|
btnAgregar | page.getByText('add', { exact: true }) | add | Abre formulario de nuevo registro |
btnGuardar | page.locator('mat-icon:has-text("save")') | save | Persiste el registro actual |
btnEditar | page.locator('mat-icon:has-text("edit")') | edit | Abre el registro en modo edición |
btnEliminar | page.locator('mat-icon:has-text("delete")') | delete | Elimina el registro seleccionado |
btnAceptar | page.getByRole('button', { name: 'Aceptar' }) | — | Confirma un diálogo de confirmación |
btnCancelar | page.getByRole('button', { name: 'Cancelar' }) | — | Cancela/cierra un diálogo |
Métodos
Cada método simplemente haceclick() sobre el locator correspondiente:
| Método | Locator invocado |
|---|---|
agregar() | btnAgregar |
guardar() | btnGuardar |
editar() | btnEditar |
eliminar() | btnEliminar |
aceptar() | btnAceptar |
cancelar() | btnCancelar |
Ejemplo
ComponentesTabla
ComponentesTabla abstrae los controles de la capa de tabla de datos del sistema: el botón de filtro que abre un diálogo, los botones de paginación, el selector de cantidad de filas por página y la selección de filas por contenido de celda.
Importación
Constructor
Locators fijos
| Propiedad | Locator | Descripción |
|---|---|---|
btnRefrescar | page.locator('mat-icon:has-text("refresh")') | Recarga la tabla desde el servidor |
btnFiltrar | page.locator('mat-icon:has-text("filter_list")') | Abre el diálogo de filtro de la tabla |
selectorFilas | page.locator('div.mat-select-arrow') | Desplegable de registros por página |
btnPrimeraPagina | page.locator('.divtable').getByRole('button', { name: 'Primer registro' }) | Salta a la primera página |
btnPaginaAnterior | page.locator('.divtable').getByRole('button', { name: 'Anterior' }) | Página anterior |
btnPaginaSiguiente | page.locator('.divtable').getByRole('button', { name: 'Siguiente' }) | Página siguiente |
btnUltimaPagina | page.locator('.divtable').getByRole('button', { name: 'Último registro' }) | Salta a la última página |
Locators dinámicos (funciones)
| Función | Retorna | Descripción |
|---|---|---|
checkboxSeleccion(textoFila) | Checkbox de la fila que contiene una gridcell con texto exactamente igual a textoFila | Evita seleccionar filas cuyo NIT es prefijo de otro |
celdaDato(textoFila) | page.getByRole('row', { name: textoFila }).getByRole('gridcell').nth(1) | Segunda celda de una fila — usada para abrir el detalle |
Métodos
filtrar()
Hace clic en btnFiltrar y espera hasta 15 s a que el diálogo de búsqueda sea visible antes de retornar.
seleccionarFila(textoFila)
Texto exacto de la celda que identifica la fila (normalmente el NIT o código del registro).
force: true) en su checkbox. El uso de coincidencia exacta de celda (getByRole('gridcell', { name: textoFila, exact: true })) evita seleccionar accidentalmente filas cuyo identificador contiene como subcadena el valor buscado.
seleccionarCantidadFilas(cantidad)
Número de filas por página a mostrar (debe coincidir exactamente con una opción del
mat-select).selectorFilas para abrir el desplegable y luego selecciona la opción cuyo texto coincide exactamente con cantidad.toString().
irAPaginaSiguiente() / irAPaginaAnterior()
Hacen clic en los botones de navegación de página correspondientes.
abrirDetalle(textoFila)
Texto de cualquier celda de la fila destino.
nth(1)) de la fila identificada por textoFila, que en los módulos del sistema equivale al campo principal del registro y abre su vista de detalle.
refrescar()
Hace clic en btnRefrescar y espera el estado networkidle (timeout 30 s) para garantizar que la tabla se haya recargado completamente.
Ejemplo completo
seleccionarFila usa force: true para que el clic en el checkbox funcione aunque un overlay de Angular Material tenga temporalmente los pointer events interceptados. Esto es un patrón recurrente con mat-bottom-sheet y cdk-global-overlay-wrapper en este proyecto.