Angular 20 BDD Cypress E2E JWT · Roles Anti SQL Injection Signals

Curso Completo de BDD
con Angular 20

5 bloques de contenido: fundamentos BDD, proyecto base, Cypress, Login con roles + CRUD, y seguridad anti-inyección SQL. Con ejemplos reales, código y rúbricas de evaluación.

Los 5 Bloques del Curso

Conceptos, código, comandos y escenarios BDD de cada bloque

🎯 Objetivos de Aprendizaje

  • Comprender el enfoque Behavior Driven Development (BDD)
  • Diferenciar TDD vs BDD
  • Entender la arquitectura base de Angular 20
  • Relacionar BDD con el flujo de desarrollo en Angular

💡 ¿Qué es BDD?

Behavior Driven Development es una metodología orientada al comportamiento del sistema desde la perspectiva del usuario. Se basa en:

  • Lenguaje ubicuo
  • Colaboración entre negocio y desarrollo
  • Escenarios en formato Given / When / Then

⚖️ TDD vs BDD

TDD BDD
Se enfoca en pruebas técnicas Se enfoca en comportamiento del negocio
Orientado al desarrollador Orientado a negocio + desarrollador
Pruebas unitarias Escenarios funcionales

📝 Sintaxis Gherkin

Feature: Gestión de usuarios
  Scenario: Crear usuario válido
    Given el usuario ingresa datos válidos
    When  presiona guardar
    Then  el sistema crea el usuario exitosamente

  Scenario: Login exitoso
    Given el usuario está autenticado
    When  hace clic en "Guardar"
    Then  el sistema persiste la información correctamente

🏗️ Angular 20 — Arquitectura

  • TypeScript como lenguaje base
  • Arquitectura modular
  • Componentes standalone (desde Angular 15+)
  • Signals — reactividad moderna
  • Servicios inyectables
  • Router integrado

🔄 Flujo de Integración BDD + Angular

1

Definir escenario en Gherkin

2

Traducir a componente Angular

3

Implementar servicio

4

Crear pruebas

📁 Estructura base Angular 20

Estructura de archivos
src/
├── main.ts              ← bootstrapApplication()
├── app.component.ts     ← componente raíz standalone
└── app.routes.ts        ← definición de rutas

⚠️ Nota

El archivo del Bloque 2 no pudo ser procesado para extracción de texto (these documents can only be used in code execution). A continuación se presenta el contenido estándar de este bloque basado en el contexto del curso.

🚀 Crear proyecto Angular 20

bash
# Instalar Angular CLI
npm install -g @angular/cli

# Crear nuevo proyecto
ng new mi-app-bdd

# Ingresar al proyecto
cd mi-app-bdd

# Ejecutar servidor de desarrollo
ng serve

🧩 Generar componentes standalone

bash
# Componente standalone
ng generate component login --standalone

ng generate component dashboard --standalone

ng generate component usuarios --standalone

# Servicio
ng generate service auth

📄 Componente standalone — estructura base

TypeScript — app.component.ts
import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    

{{ titulo() }}

`
}) export class AppComponent { titulo = signal('Angular 20 BDD'); }

🗺️ Configuración de rutas — app.routes.ts

TypeScript — app.routes.ts
import { Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { DashboardComponent } from './dashboard/dashboard.component';

export const routes: Routes = [
  { path: '',          component: LoginComponent },
  { path: 'dashboard', component: DashboardComponent },
];

🌲 ¿Qué es Cypress?

  • Herramienta de pruebas E2E basada en navegador real
  • Simula interacción del usuario
  • Valida el DOM en tiempo real
  • Intercepta requests HTTP
  • Ejecuta pruebas automatizadas

📦 Instalación de Cypress

bash
# Instalar Cypress como dependencia de desarrollo
npm install cypress --save-dev

# Abrir interfaz gráfica de Cypress
npx cypress open
JSON — package.json (scripts)
{
  "scripts": {
    "cy:open": "cypress open",
    "cy:run":  "cypress run"
  }
}

📁 Estructura de carpetas Cypress

Estructura
cypress/
└── e2e/
    └── login.cy.ts    ← pruebas E2E

📝 Escenario BDD — Autenticación

Feature: Autenticación
  Scenario: Login exitoso
    Given el usuario abre la página
    When  ingresa credenciales válidas
    Then  visualiza el dashboard

💻 Prueba BDD Login — Cypress

TypeScript — cypress/e2e/login.cy.ts
describe('Login Feature', () => {

  it('Usuario válido inicia sesión', () => {
    cy.visit('http://localhost:4200');

    cy.get('input[type=email]').type('admin@test.com');
    cy.get('input[type=password]').type('1234');
    cy.contains('Ingresar').click();

    cy.contains('Bienvenido al sistema').should('be.visible');
  });

});

🔄 Relación BDD → Código → Cypress

1

Escenario Gherkin

Redactar el comportamiento esperado en lenguaje natural

2

Implementación Angular

Crear el componente y servicio que satisface el escenario

3

Validación Cypress

Ejecutar la prueba E2E que verifica el comportamiento real

⚠️ Nota

El archivo del Bloque 4 no pudo ser procesado para extracción de texto (these documents can only be used in code execution). Se presenta el contenido completo basado en el contexto del curso.

📝 Escenarios BDD — Login con Roles

Feature: Sistema de Login con Roles

  Scenario: Login exitoso como administrador
    Given el usuario está en la página de login
    When  ingresa email "admin@test.com" y password "Admin123!"
    And   hace clic en el botón "Ingresar"
    Then  debe ver el dashboard de administrador
    And   debe ver el menú de gestión de usuarios

  Scenario: Login fallido con credenciales incorrectas
    Given el usuario está en la página de login
    When  ingresa email "user@test.com" y password "wrong"
    Then  debe ver el mensaje "Credenciales inválidas"

  Scenario: Crear usuario (solo admin)
    Given el admin está autenticado
    When  completa el formulario de nuevo usuario
    And   hace clic en "Guardar"
    Then  el usuario aparece en la lista

💻 auth.service.ts — JWT + Roles

TypeScript — auth.service.ts
import { Injectable, signal } from '@angular/core';

interface User {
  email: string;
  role: 'admin' | 'user';
}

@Injectable({ providedIn: 'root' })
export class AuthService {

  private users: User[] = [
    { email: 'admin@test.com', role: 'admin' },
    { email: 'user@test.com',  role: 'user'  },
  ];

  currentUser = signal<User | null>(null);

  login(email: string, password: string): boolean {
    const user = this.users.find(u => u.email === email);
    if (user && password === '1234') {
      this.currentUser.set(user);
      return true;
    }
    return false;
  }

  logout() { this.currentUser.set(null); }

  isAdmin(): boolean {
    return this.currentUser()?.role === 'admin';
  }
}

🔐 login.component.ts — Signals

TypeScript — login.component.ts
import { Component, signal, inject } from '@angular/core';
import { Router } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [FormsModule],
  template: `
    
    
    
    

{{ message() }}

`
}) export class LoginComponent { private auth = inject(AuthService); private router = inject(Router); email = signal(''); password = signal(''); message = signal(''); login() { const ok = this.auth.login(this.email(), this.password()); if (ok) { this.router.navigate(['/dashboard']); } else { this.message.set('Credenciales inválidas'); } } }

🛡️ Guard de roles — auth.guard.ts

TypeScript — auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';

export const authGuard: CanActivateFn = () => {
  const auth   = inject(AuthService);
  const router = inject(Router);
  if (auth.currentUser()) return true;
  return router.createUrlTree(['/']);
};

export const adminGuard: CanActivateFn = () => {
  const auth   = inject(AuthService);
  const router = inject(Router);
  if (auth.isAdmin()) return true;
  return router.createUrlTree(['/dashboard']);
};

🌲 Prueba Cypress — Login con Roles

TypeScript — cypress/e2e/login.cy.ts
describe('Login con Roles', () => {

  it('admin accede al dashboard', () => {
    cy.visit('/login');
    cy.get('[data-cy=email]').type('admin@test.com');
    cy.get('[data-cy=password]').type('Admin123!');
    cy.get('[data-cy=submit]').click();
    cy.url().should('include', '/dashboard');
    cy.get('[data-cy=admin-menu]').should('be.visible');
  });

  it('credenciales incorrectas muestran error', () => {
    cy.visit('/login');
    cy.get('[data-cy=email]').type('user@test.com');
    cy.get('[data-cy=password]').type('wrong');
    cy.get('[data-cy=submit]').click();
    cy.contains('Credenciales inválidas').should('be.visible');
  });

  it('usuario sin rol admin no ve gestión de usuarios', () => {
    cy.visit('/login');
    cy.get('[data-cy=email]').type('user@test.com');
    cy.get('[data-cy=password]').type('1234');
    cy.get('[data-cy=submit]').click();
    cy.get('[data-cy=admin-menu]').should('not.exist');
  });

});

🛡️ Buenas Prácticas de Seguridad

  • Nunca confiar en el frontend
  • Validar siempre en el backend
  • Usar ORM con parámetros preparados
  • Sanitizar todas las entradas
  • Principio de mínimo privilegio

📝 Escenario BDD — Seguridad

Feature: Seguridad Login
  Scenario: Intento de inyección SQL
    Given el usuario escribe
          "admin' OR 1=1 --"
    When  intenta iniciar sesión
    Then  el sistema rechaza la entrada

💻 Validación Frontend — login.component.ts

TypeScript — login.component.ts (fragmento)
login() {
  const email = this.email();

  // Patrón de detección de SQL Injection
  const sqlInjectionPattern = /('|--|;|DROP|SELECT|INSERT)/i;

  if (sqlInjectionPattern.test(email)) {
    this.message.set('Entrada inválida detectada');
    return;
  }

  const isValid = this.authService.login(email, this.password());
  this.message.set(isValid ? 'Bienvenido' : 'Acceso denegado');
}

🗄️ Backend Seguro — Consultas Parametrizadas

TypeScript — backend (simulado)
// ❌ INSEGURO — concatenación directa
const query = `SELECT * FROM users WHERE email = '${email}'`;

// ✅ SEGURO — consulta parametrizada
const query = 'SELECT * FROM users WHERE email = ?';
db.execute(query, [email]);

// ✅ SEGURO — con ORM (TypeORM ejemplo)
const user = await userRepository.findOne({
  where: { email }   // ORM maneja la parametrización
});

🌲 Prueba Cypress — SQL Injection

TypeScript — cypress/e2e/security.cy.ts
describe('Seguridad Anti SQL Injection', () => {

  it('Bloquea intento SQL Injection', () => {
    cy.visit('/');
    cy.get('input[type=email]').type("admin' OR 1=1 --");
    cy.contains('Ingresar').click();
    cy.contains('Entrada inválida detectada').should('exist');
  });

  it('Bloquea intento con DROP TABLE', () => {
    cy.visit('/');
    cy.get('input[type=email]').type("'; DROP TABLE users; --");
    cy.contains('Ingresar').click();
    cy.contains('Entrada inválida detectada').should('exist');
  });

});

Ejemplo Práctico Completo

Sistema de Login con BDD en Angular 20 — evidencias esperadas

📝 Escenario BDD del Ejemplo

Feature: Autenticación
  Scenario: Usuario válido inicia sesión
    Given el usuario ingresa credenciales válidas
    When  presiona el botón ingresar
    Then  el sistema muestra mensaje de acceso correcto

Evidencias Esperadas

  • Componente standalone implementado
  • Uso correcto de signal()
  • Servicio separado (AuthService)
  • Mensaje reactivo con signals
  • Prueba Cypress ejecutable

🏗️ Arquitectura del Ejemplo

Estructura
src/app/
├── login/
│   └── login.component.ts
├── dashboard/
│   └── dashboard.component.ts
├── auth.service.ts
└── app.routes.ts

cypress/e2e/
└── login.cy.ts

Rúbricas de Evaluación

Criterios completos tal como están definidos en el curso

📋 Rúbrica 1 — Ejemplo Práctico BDD Angular 20

Criterio Excelente (5) Bueno (4) Regular (3) Deficiente (1-2)
Redacción BDD Escenarios claros y completos Escenarios comprensibles Escenarios incompletos No aplica formato Given/When/Then
Arquitectura Angular Uso correcto de standalone + servicios Separación parcial Lógica mezclada Sin estructura clara
Uso de Signals Uso correcto y reactivo Uso básico Uso incorrecto No utiliza signals
Implementación Funcional Funciona completamente Funciona con detalles menores Errores visibles No funciona
Buenas Prácticas Código limpio y organizado Código aceptable Código desordenado Código caótico
21–25
Dominio alto
16–20
Competente
11–15
En proceso
0–10
Reforzar fundamentos

Puntaje Total: 25 puntos

🔐 Rúbrica 2 — Bloque 4: Login, Roles y CRUD

Criterio Excelente (5) Bueno (4) Regular (3) Deficiente (1-2)
Implementación Login Funcional con roles correctos Funciona parcialmente Errores menores No funciona
Protección por Rol Restricción correcta Restricción parcial Lógica incompleta Sin control
CRUD Crear y eliminar correctos Funciona con fallos Inconsistente No implementado
Uso Signals Reactividad correcta Uso básico Uso incorrecto No usa signals
Prueba Cypress Escenario completo Parcial Prueba incompleta No existe
21–25
Dominio Alto
16–20
Competente
11–15
En proceso
0–10
Reforzar fundamentos

Puntaje máximo: 25 puntos

🛡️ Rúbrica 3 — Bloque 5: Seguridad Anti SQL Injection

Criterio Excelente (5) Bueno (4) Regular (3) Deficiente (1-2)
Validación Anti-Inyección Patrón correcto y funcional Parcial Lógica incompleta No implementado
Buenas Prácticas Justifica técnicamente Menciona conceptos Superficial No aplica
Escenario BDD Claro y verificable Parcial Incompleto No estructurado
Prueba Cypress Detecta correctamente Funciona parcialmente Inestable No existe
Separación de Responsabilidades Código limpio Aceptable Mezclado Desordenado
21–25
Seguridad sólida
16–20
Adecuado
11–15
Riesgo medio
0–10
Crítico

Puntaje máximo: 25 puntos