FactuGES_ERP/docs/frontend/Frontend-ERP-Convencion-tests-E2E.md

452 lines
8.2 KiB
Markdown
Raw Normal View History

# Frontend ERP – Convención de organización y naming de tests E2E
**Estado:** convención adoptada para Playwright y la suite E2E del ERP.
## 1. Propósito
Definir una convención única para organizar, nombrar e identificar los tests E2E del ERP, de forma que sean fáciles de localizar, mantener y relacionar con el módulo y la feature funcional que validan.
## 2. Principio de organización
El primer nivel de directorios bajo `tests/e2e` debe corresponder exactamente con el nombre del módulo propietario existente bajo `/modules`.
Regla:
```txt
tests/e2e/<module>/
```
Ejemplos:
```txt
modules/identity
→ tests/e2e/identity
modules/customers
→ tests/e2e/customers
modules/companies
→ tests/e2e/companies
modules/catalogs
→ tests/e2e/catalogs
modules/customer-invoices
→ tests/e2e/customer-invoices
```
No deben crearse categorías E2E alternativas como:
```txt
auth/
invoices/
settings/
```
si no coinciden con el módulo propietario real.
## 3. Convención de nombres de fichero
El nombre del fichero debe representar la **feature o el flujo funcional probado**. No debe incluir numeración de casos.
Ejemplos:
```txt
login.spec.ts
protected-routes.spec.ts
logout.spec.ts
create-customer.spec.ts
update-customer.spec.ts
```
Se adopta `*.spec.ts` como sufijo estándar.
Al vivir dentro de:
```txt
tests/e2e/
```
no es necesario añadir `e2e` al nombre del fichero.
## 4. Convención de identificadores de casos
Cada test funcional debe tener un identificador estable con este formato:
```txt
<MODULE>-<FEATURE>-<NNN>
```
Donde:
- `MODULE`: nombre lógico del módulo propietario, en mayúsculas.
- `FEATURE`: feature o flujo funcional que agrupa el caso.
- `NNN`: secuencia numérica de tres dígitos comenzando en `001` dentro de la feature.
Ejemplos:
```txt
IDENTITY-LOGIN-001
IDENTITY-LOGIN-002
IDENTITY-ROUTES-001
IDENTITY-LOGOUT-001
```
## 5. Ejemplo: Identity
Estructura:
```txt
tests/e2e/identity/
├── login.spec.ts
├── protected-routes.spec.ts
└── logout.spec.ts
```
Casos:
```txt
IDENTITY-LOGIN-001
Login correcto y persistencia tras recarga
IDENTITY-LOGIN-002
Rechazo de credenciales incorrectas
IDENTITY-ROUTES-001
Acceso a ruta protegida sin sesión
IDENTITY-LOGOUT-001
Cierre de sesión
```
Ejemplo en Playwright:
```ts
test(
"IDENTITY-LOGIN-001: login correcto y persistencia tras recarga",
async ({ page }) => {
// ...
},
);
test(
"IDENTITY-LOGIN-002: rechazo de credenciales incorrectas",
async ({ page }) => {
// ...
},
);
```
## 6. Ejemplos en otros módulos
### Customers
```txt
tests/e2e/customers/create-customer.spec.ts
→ CUSTOMERS-CREATE-001
→ CUSTOMERS-CREATE-002
tests/e2e/customers/update-customer.spec.ts
→ CUSTOMERS-UPDATE-001
```
Ejemplos:
```txt
CUSTOMERS-CREATE-001
Alta válida de cliente
CUSTOMERS-CREATE-002
Validaciones de alta
CUSTOMERS-UPDATE-001
Modificación válida de cliente
```
### Customer Invoices
Para módulos grandes se permite añadir la bounded feature al identificador:
```txt
tests/e2e/customer-invoices/proformas/create-proforma.spec.ts
→ CUSTOMER-INVOICES-PROFORMA-CREATE-001
```
No deben introducirse abreviaturas arbitrarias diferentes para el mismo módulo.
Evitar mezclar, por ejemplo:
```txt
CUSTOMER-INVOICES-...
INVOICES-...
CI-...
```
salvo que en el futuro se acuerde formalmente una abreviatura estable.
## 7. Subdirectorios en módulos grandes
Un módulo grande puede subdividirse por bounded feature, pero el primer nivel bajo `tests/e2e` continúa siendo siempre el módulo propietario.
Ejemplo:
```txt
tests/e2e/customer-invoices/
├── proformas/
├── issued-invoices/
└── recurring-proformas/
```
Correcto:
```txt
tests/e2e/customer-invoices/proformas/
tests/e2e/customer-invoices/issued-invoices/
tests/e2e/customer-invoices/recurring-proformas/
```
Evitar:
```txt
tests/e2e/proformas/
tests/e2e/issued-invoices/
tests/e2e/recurring-proformas/
```
porque perdería la trazabilidad directa con:
```txt
modules/customer-invoices
```
## 8. Reglas de numeración
La numeración:
- se reinicia por feature;
- utiliza siempre tres dígitos: `001`, `002`, `003`...;
- identifica el caso, no su prioridad;
- no determina el orden de ejecución;
- no debe cambiar por reordenar tests dentro del fichero;
- no debe reutilizarse para representar otro escenario funcional diferente.
Ejemplo:
```txt
IDENTITY-LOGIN-001
IDENTITY-LOGIN-002
IDENTITY-LOGIN-003
IDENTITY-ROUTES-001
IDENTITY-ROUTES-002
IDENTITY-LOGOUT-001
```
No usar una única secuencia global como:
```txt
IDENTITY-001
IDENTITY-002
IDENTITY-003
...
```
porque pierde información sobre la feature.
## 9. Naming del test
El identificador debe formar parte del título de `test()`.
Correcto:
```ts
test("IDENTITY-LOGIN-002: rechazo de credenciales incorrectas", async ({
page,
}) => {
// ...
});
```
Evitar:
```ts
test("rechaza credenciales incorrectas", async ({ page }) => {
// ...
});
```
si ese test pertenece al catálogo E2E formal.
El texto posterior al identificador debe describir el comportamiento desde el punto de vista funcional.
## 10. Localización de tests
La inclusión del identificador en el título permite localizar cualquier caso mediante búsqueda textual.
Ejemplo:
```bash
rg -n --glob "*.spec.ts" "IDENTITY-LOGIN-002" tests/e2e
```
Para localizar todos los casos de una feature:
```bash
rg -n --glob "*.spec.ts" "IDENTITY-LOGIN-" tests/e2e
```
Para obtener solo los ficheros:
```bash
rg -l --glob "*.spec.ts" "IDENTITY-LOGIN-002" tests/e2e
```
## 11. Convención de selectores E2E
Los tests no deben depender de textos traducidos.
Orden de preferencia:
```txt
1. id técnico estable
2. data-testid estable
3. atributo técnico estable, por ejemplo name
4. selector CSS técnico y estable, solo excepcionalmente
```
Ejemplos:
```ts
page.locator("#login-email");
page.getByTestId("login-submit");
page.locator('input[name="email"]');
```
Evitar como contrato principal:
```ts
page.getByText("Entrar");
page.getByLabel("Email");
page.getByRole("button", { name: "Entrar" });
```
porque esos textos pueden variar con el locale.
También evitar:
```txt
clases Tailwind visuales
nth-child
selectores dependientes de posición
estructura DOM incidental
IDs generados mediante React.useId()
```
Las clases CSS solo deben utilizarse excepcionalmente cuando sean clases técnicas deliberadamente estables, no clases de presentación.
## 12. Accesibilidad
La estrategia de selectores E2E no sustituye la accesibilidad.
Los componentes deben mantener correctamente:
```txt
label
htmlFor
role
aria-label
aria-describedby
aria-live
```
cuando corresponda.
Los atributos técnicos para testing son un contrato complementario.
## 13. Independencia de los tests
Cada caso E2E debe poder ejecutarse:
```txt
de forma aislada
en cualquier orden
junto con el resto de la suite
```
El identificador:
```txt
IDENTITY-LOGIN-002
```
no significa que el test dependa de:
```txt
IDENTITY-LOGIN-001
```
Los tests no deben depender del estado dejado por casos anteriores.
## 14. Relación entre directorio, fichero y caso
Regla general:
```txt
Directorio
→ módulo propietario
Fichero
→ feature o flujo funcional
Caso
→ MODULE-FEATURE-NNN
```
Ejemplo:
```txt
tests/e2e/identity/login.spec.ts
→ IDENTITY-LOGIN-001
→ IDENTITY-LOGIN-002
tests/e2e/identity/protected-routes.spec.ts
→ IDENTITY-ROUTES-001
tests/e2e/identity/logout.spec.ts
→ IDENTITY-LOGOUT-001
```
## 15. Criterio para nuevas features
Antes de crear un nuevo test:
1. identificar el módulo propietario real bajo `/modules`;
2. localizar o crear el fichero correspondiente al flujo funcional;
3. determinar el nombre estable de la feature;
4. asignar el siguiente `NNN` disponible dentro de esa feature;
5. incluir el identificador completo en el título de `test()`.
No crear nuevos prefijos para pequeñas variaciones del mismo flujo si no existe una diferencia funcional clara.
## 16. Regla mental final
```txt
tests/e2e/<module>/<feature>.spec.ts
<MODULE>-<FEATURE>-<NNN>
```
Ejemplo:
```txt
tests/e2e/identity/login.spec.ts
IDENTITY-LOGIN-001
```
La estructura física indica **quién es propietario del comportamiento**.
El fichero indica **qué flujo se está probando**.
El identificador indica **qué caso funcional concreto se está ejecutando**.