452 lines
8.2 KiB
Markdown
452 lines
8.2 KiB
Markdown
# 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**.
|