9.0 KiB
Reporte de Pruebas E2E — Flujo de Factura
Proyecto: Anexo 76 — Sistema de Control de Operaciones Aduaneras
Herramienta: Playwright
Archivo: frontend/e2e/invoice-flow.spec.ts
Fecha: Abril 2026
Estado: 12/12 pruebas pasando ✅
Tiempo de ejecución: ~4.4 minutos
Resumen
| Categoría | Pruebas | Estado |
|---|---|---|
| Prerrequisitos (proveedor, cliente, agente, TC) | 4 | ✅ |
| Pedimento | 1 | ✅ |
| Factura TEM | 3 | ✅ |
| Partidas | 2 | ✅ |
| Actualización final | 1 | ✅ |
| Total | 11 | ✅ |
Nota: el test de setup de autenticación (
auth.setup.ts) suma 1 prueba adicional, totalizando 12 en el runner.
Flujo completo
1. Crear proveedor
Navega a /dashboard/clients_and_providers, abre el formulario de nuevo socio, llena RFC y nombre, selecciona tipo "Proveedor" y guarda. Verifica redirección a la lista y que el nombre aparece en la tabla.
2. Crear cliente
Mismo flujo que el proveedor pero con tipo "Cliente".
3. Crear agente aduanal
Navega a /dashboard/customs_brokers, abre el formulario, llena clave, patente y nombre. Verifica toast de éxito y redirección.
4. Crear tipo de cambio
Navega a /dashboard/general_catalogs/exchange-rate, abre el modal de nuevo tipo de cambio, llena fecha de hoy y valor 17.5, confirma. Verifica toast de éxito.
5. Crear pedimento
Navega a /dashboard/pedimentos/edit/new, llena año (26), selecciona Aduana, Patente, Clave, Tipo de Operación y Régimen via bits-ui Select. Llena número de pedimento. Guarda y verifica redirección a /dashboard/pedimentos.
6. Crear factura de importación TEM
Navega a /dashboard/invoices/edit/new?operation_type=imp&invoice_type=TEM. Llena número de factura con pressSequentially, fecha, y en la pestaña General selecciona proveedor, sold-to, shipped-to, agente aduanal, aduana y tipo de documento. Guarda y verifica toast de éxito. Al finalizar guarda el número de factura en .e2e-shared.json para los tests posteriores.
7. Factura aparece en la lista
Filtra por número de factura en la lista de importación y verifica que la fila es visible.
8. Agregar partida a la factura
Abre la factura desde la lista, navega a la pestaña Partidas, abre el sheet de nueva partida. Selecciona Clase, U.M. y País de Origen (cada uno abre un dialog con tabla). Llena cantidad (10), costo unitario (100), peso neto (5), peso bruto (6) y descripción en español. Hace click en el botón "Crear" del sheet. Guarda la factura completa.
9. Editar factura existente
Lee el número de factura desde .e2e-shared.json, la busca en la lista, la abre en modo edición. En la pestaña General vuelve a seleccionar agente aduanal, aduana y tipo de documento. Guarda y verifica toast de éxito.
10. Editar partida existente
Lee el número desde shared, abre la factura, va a pestaña Partidas. Hace click en el ícono Pencil de la primera fila para abrir el sheet de edición. Modifica cantidad (20) y costo unitario (200). Hace click en "Actualizar" del sheet. Guarda la factura.
11. Actualizar factura — verificación final
Lee el número desde shared, busca la factura en la lista, selecciona la fila, hace click en el botón "Actualizar" del footer (ícono RefreshCw, clase h-8). Verifica el resultado con toast de éxito.
Patrones técnicos establecidos
fillInput — inputs reactivos de Svelte 5
Los inputs de Svelte 5 no responden a page.fill() ni pressSequentially de forma confiable. La solución es usar el native setter del prototipo:
async function fillInput(page: Page, selector: string, value: string) {
await page.locator(selector).click()
await page.evaluate(({ sel, val }) => {
const el = document.querySelector(sel) as HTMLInputElement
const setter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype, 'value'
)?.set
setter?.call(el, val)
el.dispatchEvent(new Event('input', { bubbles: true }))
el.dispatchEvent(new Event('change', { bubbles: true }))
}, { sel: selector, val: value })
await page.waitForTimeout(2000)
}
La excepción es #invoice_number, que sí responde a pressSequentially con delay:
await page.locator('#invoice_number').click()
await page.keyboard.press('Control+A')
await page.locator('#invoice_number').pressSequentially(INVOICE_NUMBER, { delay: 1000 })
bits-ui Select — selects con IDs dinámicos
Los selects de bits-ui generan IDs como bits-s65 que cambian en cada render. La estrategia es seleccionarlos por el atributo data-select-trigger y posición:
const triggers = page.locator('[data-select-trigger]')
await triggers.nth(0).click() // Aduana
await page.getByRole('option').first().click()
Para selects con IDs estables (facturas) se usa directamente:
await page.locator('#provider_id').click()
await page.getByRole('option').first().click()
Dialogs anidados — clase, U.M., país de origen
Los campos Clase, U.M. y País de Origen abren un dialog de búsqueda encima del sheet. Para evitar que el sheet intercepte los clicks, se scopea al último dialog abierto:
await page.locator('#clase').click()
await page.waitForTimeout(3000)
const claseDialog = page.locator('[data-dialog-content]').last()
await claseDialog.locator('tbody tr').first().click()
Botones dentro del sheet
El botón de guardar partida está dentro del sheet y puede ser interceptado. Se scopea explícitamente:
const sheet = page.locator('[data-slot="sheet-content"]')
await sheet.getByRole('button', { name: /Crear/ }).click() // nueva partida
await sheet.getByRole('button', { name: /Actualizar/ }).click() // editar partida
Distinguir botones ambiguos por clase CSS
Cuando hay múltiples botones con el mismo texto o ícono, se distinguen por clases CSS únicas:
// Botón Actualizar del footer (tiene h-8, border, RefreshCw)
await page.locator('button.h-8:has([class*="lucide-refresh"])').click()
Compartir estado entre tests
Playwright corre cada test en un worker separado, por lo que Date.now() se reevalúa. Para compartir el número de factura entre tests se usa un archivo JSON:
// Al final del test 6
saveShared({ INVOICE_NUMBER })
// En tests 7-11
const shared = loadShared()
const invoiceNumber = shared.INVOICE_NUMBER || INVOICE_NUMBER
El archivo se guarda en frontend/e2e/.e2e-shared.json.
Selectores de referencia
| Campo | Selector | Tipo |
|---|---|---|
| RFC | #rfc |
input normal |
| Nombre | #name |
input normal |
| Tipo de socio | #type |
bits-ui Select |
| Año pedimento | #year |
input normal |
| Número pedimento | #pedimento_number |
input normal |
| Aduana pedimento | [data-select-trigger] nth(0) |
bits-ui Select |
| Patente pedimento | [data-select-trigger] nth(1) |
bits-ui Select |
| Clave pedimento | [data-select-trigger] nth(2) |
bits-ui Select |
| Número factura | #invoice_number |
input (pressSequentially) |
| Fecha factura | #invoice_date |
date input |
| Proveedor | #provider_id |
bits-ui Select |
| Sold-to | #sold_to_id |
bits-ui Select |
| Shipped-to | #shipped_to_id |
bits-ui Select |
| Agente aduanal | #customs_broker_id |
bits-ui Select |
| Aduana factura | #aduana |
bits-ui Select |
| Tipo documento | #document_type |
bits-ui Select |
| Clase partida | #clase |
input readonly → dialog |
| U.M. | #um |
input readonly → dialog |
| País origen | #pais_origen |
input readonly → dialog |
| Cantidad | #cantidad |
input number |
| Costo unitario | #costo_unitario |
input number |
| Peso neto | #peso_neto |
input number |
| Peso bruto | #peso_bruto |
input number |
| Descripción ES | #desc_espanol |
textarea |
| Filtro número | #filter-invoice-number |
input normal |
Comandos
# Flujo completo
pnpm test:e2e --grep "Flujo completo"
# Test individual
pnpm test:e2e --grep "5. crear pedimento"
pnpm test:e2e --grep "8. agregar partida"
# Modo visual para debug
pnpm test:e2e --grep "Flujo completo" --headed --timeout 120000
Estructura de archivos
frontend/e2e/
├── .auth/
│ └── user.json sesion de autenticacion
├── .e2e-shared.json estado compartido entre tests (generado)
├── auth.setup.ts 1 test — login y guardado de sesion
├── invoice-flow.spec.ts 11 tests — flujo completo de factura
├── full-flow.spec.ts 4 tests
├── login.spec.ts 3 tests
├── navigation.spec.ts 10 tests
└── modules.spec.ts 10 tests
Conteo total actualizado
| Suite | Pruebas |
|---|---|
| auth.setup.ts | 1 |
| invoice-flow.spec.ts | 11 |
| full-flow.spec.ts | 4 |
| login.spec.ts | 3 |
| navigation.spec.ts | 10 |
| modules.spec.ts | 10 |
| Total Playwright | 39 |
Anexo 76 — Reporte de Pruebas E2E v4.0 — invoice-flow — Abril 2026