feat: plantilla base workspace SaaS
This commit is contained in:
248
frontend/e2e/FlujoCompleto.MD
Normal file
248
frontend/e2e/FlujoCompleto.MD
Normal file
@@ -0,0 +1,248 @@
|
||||
# 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:
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```bash
|
||||
# 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*
|
||||
Reference in New Issue
Block a user