# 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// ``` 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 -- ``` 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//.spec.ts -- ``` 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**.