spr migrated books, and tester
This commit is contained in:
470
atlas/book/feature-flow/index-es.html
Normal file
470
atlas/book/feature-flow/index-es.html
Normal file
@@ -0,0 +1,470 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Feature Flow - Pipeline de Estandarizacion</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.slide {
|
||||
display: none;
|
||||
min-height: 100vh;
|
||||
padding: 3rem;
|
||||
}
|
||||
.slide.active { display: flex; flex-direction: column; }
|
||||
.nav {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
z-index: 100;
|
||||
}
|
||||
.nav button {
|
||||
background: #334155;
|
||||
border: none;
|
||||
color: #e2e8f0;
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.nav button:hover { background: #475569; }
|
||||
.nav .counter {
|
||||
background: transparent;
|
||||
padding: 0.75rem 1rem;
|
||||
color: #64748b;
|
||||
}
|
||||
.slide-title {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.slide-title h1 {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
background: linear-gradient(135deg, #6366f1, #a855f7);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.slide-title p { font-size: 1.5rem; color: #94a3b8; }
|
||||
.slide-title .subtitle { margin-top: 3rem; font-size: 1rem; color: #64748b; }
|
||||
.pipeline {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
.pipeline-step {
|
||||
background: #1e293b;
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
.pipeline-step.ops { border-color: #22c55e; }
|
||||
.pipeline-step.bdd { border-color: #6366f1; }
|
||||
.pipeline-step.tests { border-color: #f59e0b; }
|
||||
.pipeline-step h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.pipeline-step .num {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.pipeline-step.ops .num { background: #22c55e; color: #0f172a; }
|
||||
.pipeline-step.bdd .num { background: #6366f1; color: white; }
|
||||
.pipeline-step.tests .num { background: #f59e0b; color: #0f172a; }
|
||||
.pipeline-step ul { list-style: none; color: #94a3b8; font-size: 0.95rem; }
|
||||
.pipeline-step li { padding: 0.4rem 0; padding-left: 1rem; border-left: 2px solid #334155; margin-bottom: 0.25rem; }
|
||||
.slide h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
.slide h2 .badge {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.slide h2 .badge.ops { background: #22c55e; color: #0f172a; }
|
||||
.slide h2 .badge.bdd { background: #6366f1; color: white; }
|
||||
.slide h2 .badge.tests { background: #f59e0b; color: #0f172a; }
|
||||
pre {
|
||||
background: #1e293b;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.keyword { color: #c084fc; }
|
||||
.string { color: #4ade80; }
|
||||
.comment { color: #64748b; }
|
||||
.decorator { color: #f59e0b; }
|
||||
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; flex: 1; }
|
||||
.col { background: #1e293b; border-radius: 8px; padding: 1.5rem; }
|
||||
.col h4 { font-size: 0.9rem; color: #94a3b8; margin-bottom: 1rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||
.checklist { list-style: none; font-size: 1.1rem; }
|
||||
.checklist li { padding: 0.75rem 0; display: flex; align-items: center; gap: 0.75rem; border-bottom: 1px solid #334155; }
|
||||
.checklist .check { width: 20px; height: 20px; border: 2px solid #475569; border-radius: 4px; }
|
||||
.flow-list { list-style: none; }
|
||||
.flow-list li { padding: 0.5rem 0; color: #94a3b8; }
|
||||
.flow-list li strong { color: #e2e8f0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="slide slide-title active" data-slide="0">
|
||||
<h1>Feature Flow</h1>
|
||||
<p>Pipeline de Estandarizacion</p>
|
||||
<div class="subtitle">Templates Ops → BDD/Gherkin → Tests Backend + Frontend</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="1">
|
||||
<h2>Vision General del Pipeline</h2>
|
||||
<div class="pipeline">
|
||||
<div class="pipeline-step ops">
|
||||
<h3><span class="num">1</span> Templates Ops</h3>
|
||||
<ul>
|
||||
<li>Lenguaje no tecnico</li>
|
||||
<li>Flujos desde el usuario</li>
|
||||
<li>Del equipo de soporte/ops</li>
|
||||
<li>Captura casos borde</li>
|
||||
<li>Documenta problemas conocidos</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pipeline-step bdd">
|
||||
<h3><span class="num">2</span> BDD/Gherkin</h3>
|
||||
<ul>
|
||||
<li>Archivos .feature</li>
|
||||
<li>Sintaxis Given/When/Then</li>
|
||||
<li>Specs legibles</li>
|
||||
<li>Fuente unica de verdad</li>
|
||||
<li>Mapea a ambos tipos de test</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pipeline-step tests">
|
||||
<h3><span class="num">3</span> Tests</h3>
|
||||
<ul>
|
||||
<li><strong>Backend:</strong> Contratos API</li>
|
||||
<li><strong>Backend:</strong> Workflows (composiciones)</li>
|
||||
<li><strong>Frontend:</strong> Page Objects</li>
|
||||
<li><strong>Frontend:</strong> E2E specs (Playwright)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="2">
|
||||
<h2><span class="badge ops">1</span> Templates Ops</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Estructura de la Plantilla</h4>
|
||||
<pre>
|
||||
<span class="keyword">### [Nombre del Flujo]</span>
|
||||
|
||||
<span class="comment">Tipo de usuario:</span> Dueno / Vet / Admin
|
||||
<span class="comment">Donde empieza:</span> Pagina/boton/link
|
||||
<span class="comment">Objetivo:</span> Una oracion
|
||||
|
||||
<span class="keyword">Pasos:</span>
|
||||
1. Primera accion
|
||||
2. Segunda accion
|
||||
3. ...
|
||||
|
||||
<span class="keyword">Que deberia pasar:</span>
|
||||
- Resultado esperado
|
||||
|
||||
<span class="keyword">Problemas comunes:</span>
|
||||
- Problema 1
|
||||
|
||||
<span class="keyword">Casos especiales:</span>
|
||||
- Caso especial 1
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Fuente</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Plantilla:</strong> album/template/ops-flow/</li>
|
||||
<li><strong>Referencia:</strong> def/work_plan/21-plantilla</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Quien Completa Esto</h4>
|
||||
<ul class="flow-list">
|
||||
<li>Equipo de soporte (contacto diario)</li>
|
||||
<li>Equipo de ops (conoce workarounds)</li>
|
||||
<li>Producto (requerimientos)</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Output</h4>
|
||||
<ul class="flow-list">
|
||||
<li>Un .md por flujo</li>
|
||||
<li>Organizado por tipo de usuario</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="3">
|
||||
<h2><span class="badge bdd">2</span> BDD/Gherkin</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Archivo .feature</h4>
|
||||
<pre>
|
||||
<span class="keyword">Feature:</span> Turnero - Reservar turno
|
||||
|
||||
<span class="keyword">Scenario:</span> Reservar vacuna para gato
|
||||
<span class="decorator">Given</span> estoy en la pagina del turnero
|
||||
<span class="decorator">When</span> ingreso direccion <span class="string">"Av Santa Fe 1234"</span>
|
||||
<span class="decorator">And</span> hago click en <span class="string">"Siguiente"</span>
|
||||
<span class="decorator">Then</span> se crea un usuario invitado
|
||||
|
||||
<span class="decorator">When</span> agrego mascota <span class="string">"Koshka"</span> tipo <span class="string">"Gato"</span>
|
||||
<span class="decorator">And</span> selecciono <span class="string">"Vacunacion"</span>
|
||||
<span class="decorator">Then</span> <span class="string">"Consulta clinica"</span> se agrega auto
|
||||
|
||||
<span class="keyword">Scenario:</span> Servicios filtrados por tipo
|
||||
<span class="decorator">Given</span> agregue un gato
|
||||
<span class="decorator">Then</span> veo vacunas felinas
|
||||
<span class="decorator">And</span> no veo vacunas caninas
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Palabras Clave</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Feature</strong> = una funcionalidad</li>
|
||||
<li><strong>Scenario</strong> = un comportamiento</li>
|
||||
<li><strong>Given</strong> = precondicion</li>
|
||||
<li><strong>When</strong> = accion</li>
|
||||
<li><strong>Then</strong> = resultado esperado</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Referencia</h4>
|
||||
<ul class="flow-list">
|
||||
<li>def/work_plan/10-flow-turnero.md</li>
|
||||
<li>Ejemplo completo con Gherkin, tests API, Page Objects</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="4">
|
||||
<h2><span class="badge bdd">2b</span> Organizacion de Archivos Gherkin</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Correcto: Una Feature = Un Archivo</h4>
|
||||
<pre>
|
||||
<span class="keyword">features/</span>
|
||||
├── pet-owner/
|
||||
│ ├── registro.feature <span class="comment"># 6-8 escenarios</span>
|
||||
│ ├── reservar-turno.feature <span class="comment"># 10-15 escenarios</span>
|
||||
│ ├── gestion-mascotas.feature
|
||||
│ └── pago.feature
|
||||
├── veterinarian/
|
||||
│ └── ...
|
||||
└── backoffice/
|
||||
└── ...
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Anti-patron: Un Escenario = Un Archivo</h4>
|
||||
<pre style="border-left: 3px solid #ef4444;">
|
||||
<span class="comment"># NO hacer esto</span>
|
||||
features/pet-owner/registro/
|
||||
├── registro-exitoso.feature
|
||||
├── registro-email-invalido.feature
|
||||
├── registro-password-corto.feature
|
||||
└── <span class="comment">... (docenas de archivos pequeños)</span>
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Por Que Multiples Escenarios por Archivo</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Feature = Capacidad</strong> - un archivo describe una capacidad con todos sus comportamientos</li>
|
||||
<li><strong>Contexto junto</strong> - Background, Rules comparten contexto</li>
|
||||
<li><strong>Tooling lo espera</strong> - test runners, reportes, navegacion IDE</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Cuando Dividir</h4>
|
||||
<pre>
|
||||
<span class="comment"># Escenarios por archivo:</span>
|
||||
5-20 <span class="string">Normal, mantener</span>
|
||||
20-40 <span class="string">Considerar dividir</span>
|
||||
40+ <span class="string">Definitivamente dividir</span>
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Profundidad de Carpetas</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Bien:</strong> 1-2 niveles max</li>
|
||||
<li><strong>Evitar:</strong> anidamiento profundo</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="5">
|
||||
<h2><span class="badge tests">3a</span> Tests Backend</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Estructura (amar_django_back)</h4>
|
||||
<pre>
|
||||
tests/contracts/
|
||||
├── base.py <span class="comment"># switcher de modo</span>
|
||||
├── endpoints.py <span class="comment"># paths API (fuente unica)</span>
|
||||
├── helpers.py <span class="comment"># datos de prueba</span>
|
||||
│
|
||||
├── mascotas/ <span class="comment"># tests por app</span>
|
||||
│ ├── test_pet_owners.py
|
||||
│ ├── test_pets.py
|
||||
│ └── test_coverage.py
|
||||
├── productos/
|
||||
│ ├── test_services.py
|
||||
│ └── test_cart.py
|
||||
├── solicitudes/
|
||||
│ └── test_service_requests.py
|
||||
│
|
||||
└── <span class="keyword">workflows/</span> <span class="comment"># composiciones</span>
|
||||
└── test_turnero_general.py
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Dos Modos de Test</h4>
|
||||
<pre>
|
||||
<span class="comment"># Rapido (Django test client)</span>
|
||||
pytest tests/contracts/
|
||||
|
||||
<span class="comment"># Live (HTTP real)</span>
|
||||
CONTRACT_TEST_MODE=live pytest
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Workflow = Composicion</h4>
|
||||
<pre>
|
||||
<span class="comment"># Llama endpoints en secuencia:</span>
|
||||
1. Check cobertura
|
||||
2. Crear pet owner
|
||||
3. Crear mascota
|
||||
4. Obtener servicios
|
||||
5. Crear solicitud
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Archivos Clave</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>endpoints.py</strong> - cambiar paths solo aca</li>
|
||||
<li><strong>helpers.py</strong> - datos de ejemplo</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="6">
|
||||
<h2><span class="badge tests">3b</span> Tests Frontend</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Estructura (amar_frontend)</h4>
|
||||
<pre>
|
||||
tests/e2e/
|
||||
├── <span class="keyword">pages/</span> <span class="comment"># Page Objects</span>
|
||||
│ ├── BasePage.ts
|
||||
│ ├── LoginPage.ts
|
||||
│ └── index.ts
|
||||
│
|
||||
└── login.spec.ts <span class="comment"># test E2E</span>
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Patron Page Object</h4>
|
||||
<pre>
|
||||
<span class="keyword">export class</span> LoginPage <span class="keyword">extends</span> BasePage {
|
||||
<span class="keyword">get</span> emailInput() {
|
||||
<span class="keyword">return</span> this.page.getByLabel(<span class="string">'Email'</span>);
|
||||
}
|
||||
|
||||
<span class="keyword">async</span> login(email, password) {
|
||||
<span class="keyword">await</span> this.emailInput.fill(email);
|
||||
<span class="keyword">await</span> this.passwordInput.fill(password);
|
||||
<span class="keyword">await</span> this.submitButton.click();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Ejecutar Tests</h4>
|
||||
<pre>
|
||||
<span class="comment"># Todos los tests</span>
|
||||
npx playwright test
|
||||
|
||||
<span class="comment"># Con UI</span>
|
||||
npx playwright test --ui
|
||||
|
||||
<span class="comment"># Archivo especifico</span>
|
||||
npx playwright test login.spec.ts
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Prioridad de Locators</h4>
|
||||
<ul class="flow-list">
|
||||
<li>1. getByRole() - botones, links</li>
|
||||
<li>2. getByLabel() - campos de form</li>
|
||||
<li>3. getByText() - texto visible</li>
|
||||
<li>4. getByTestId() - data-testid</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1rem;">Evitar</h4>
|
||||
<ul class="flow-list">
|
||||
<li>Selectores de clases CSS</li>
|
||||
<li>XPath complejos</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="7">
|
||||
<h2>Checklist por Feature</h2>
|
||||
<ul class="checklist">
|
||||
<li><span class="check"></span> Template ops completado (equipo soporte)</li>
|
||||
<li><span class="check"></span> Convertir a archivo .feature (spec Gherkin)</li>
|
||||
<li><span class="check"></span> Backend: Tests de contrato API por endpoint</li>
|
||||
<li><span class="check"></span> Backend: Test workflow (composicion)</li>
|
||||
<li><span class="check"></span> Frontend: Page Object (si es pagina nueva)</li>
|
||||
<li><span class="check"></span> Frontend: E2E spec (Playwright)</li>
|
||||
<li><span class="check"></span> Conectar a CI</li>
|
||||
</ul>
|
||||
<div style="margin-top: 2rem; color: #64748b; font-size: 0.9rem;">
|
||||
<p><strong>Ejemplo completo:</strong> def/work_plan/10-flow-turnero.md</p>
|
||||
<p><strong>README Backend:</strong> amar_django_back/tests/contracts/README.md</p>
|
||||
<p><strong>README Frontend:</strong> amar_frontend/tests/README.md</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="nav">
|
||||
<button onclick="prevSlide()">←</button>
|
||||
<span class="counter"><span id="current">1</span>/<span id="total">8</span></span>
|
||||
<button onclick="nextSlide()">→</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let current = 0;
|
||||
const slides = document.querySelectorAll('.slide');
|
||||
const total = slides.length;
|
||||
document.getElementById('total').textContent = total;
|
||||
function showSlide(n) {
|
||||
slides.forEach(s => s.classList.remove('active'));
|
||||
current = (n + total) % total;
|
||||
slides[current].classList.add('active');
|
||||
document.getElementById('current').textContent = current + 1;
|
||||
}
|
||||
function nextSlide() { showSlide(current + 1); }
|
||||
function prevSlide() { showSlide(current - 1); }
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'ArrowRight' || e.key === ' ') nextSlide();
|
||||
if (e.key === 'ArrowLeft') prevSlide();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user