spr migrated books, and tester
This commit is contained in:
60
atlas/book/feature-flow/CLAUDE.md
Normal file
60
atlas/book/feature-flow/CLAUDE.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Feature Flow Book
|
||||
|
||||
## Purpose
|
||||
Presentation showing the feature standardization pipeline.
|
||||
|
||||
## The Pipeline
|
||||
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
||||
│ OPS TEMPLATES │ -> │ BDD/GHERKIN │ -> │ TESTS │
|
||||
│ │ │ │ │ │
|
||||
│ Non-technical │ │ .feature files │ │ Backend: │
|
||||
│ User flows │ │ Given/When/Then │ │ API contracts │
|
||||
│ From support │ │ Human readable │ │ Workflows │
|
||||
│ │ │ │ │ Frontend: │
|
||||
│ │ │ │ │ Page Objects │
|
||||
│ │ │ │ │ E2E specs │
|
||||
└──────────────────┘ └──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## Files
|
||||
- `index-en.html` - English slide presentation (8 slides, arrow keys)
|
||||
- `index-es.html` - Spanish slide presentation (8 slides, arrow keys)
|
||||
|
||||
## Slides Structure
|
||||
1. Title
|
||||
2. Pipeline Overview (3 columns)
|
||||
3. Ops Templates
|
||||
4. BDD/Gherkin
|
||||
5. Gherkin File Organization (best practices)
|
||||
6. Backend Tests (amar_django_back structure)
|
||||
7. Frontend Tests (amar_frontend structure)
|
||||
8. Per-Feature Checklist
|
||||
|
||||
## Sources
|
||||
|
||||
### Ops Templates
|
||||
- `album/template/ops-flow/plantilla-flujo.md`
|
||||
- `def/work_plan/21-plantilla-flujos-usuario.md`
|
||||
|
||||
### BDD/Gherkin Examples
|
||||
- `def/work_plan/10-flow-turnero.md` (full gherkin + tests example)
|
||||
|
||||
### Test Structure References
|
||||
- `amar_django_back/tests/contracts/README.md`
|
||||
- `amar_frontend/tests/README.md`
|
||||
|
||||
## Editing
|
||||
Edit `index-en.html` or `index-es.html` directly.
|
||||
Slides are `<section>` elements. Arrow keys to navigate.
|
||||
|
||||
## Flow Checklist (per feature)
|
||||
|
||||
- [ ] Ops template filled by support team
|
||||
- [ ] Convert to .feature file (Gherkin spec)
|
||||
- [ ] Backend: API contract tests per endpoint
|
||||
- [ ] Backend: Workflow test (composition)
|
||||
- [ ] Frontend: Page Object (if new page)
|
||||
- [ ] Frontend: E2E spec (Playwright)
|
||||
- [ ] Wire to CI
|
||||
392
atlas/book/feature-flow/index-en.html
Normal file
392
atlas/book/feature-flow/index-en.html
Normal file
@@ -0,0 +1,392 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Feature Flow - Standardization Pipeline</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.8rem; line-height: 1.5; 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.85rem; 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.4rem 0; color: #94a3b8; font-size: 0.9rem; }
|
||||
.flow-list li strong { color: #e2e8f0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="slide slide-title active" data-slide="0">
|
||||
<h1>Feature Flow</h1>
|
||||
<p>Standardization Pipeline</p>
|
||||
<div class="subtitle">Ops Templates → BDD/Gherkin → Backend + Frontend Tests</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="1">
|
||||
<h2>Pipeline Overview</h2>
|
||||
<div class="pipeline">
|
||||
<div class="pipeline-step ops">
|
||||
<h3><span class="num">1</span> Ops Templates</h3>
|
||||
<ul>
|
||||
<li>Non-technical language</li>
|
||||
<li>User perspective flows</li>
|
||||
<li>From support/ops team</li>
|
||||
<li>Captures edge cases</li>
|
||||
<li>Documents known problems</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pipeline-step bdd">
|
||||
<h3><span class="num">2</span> BDD/Gherkin</h3>
|
||||
<ul>
|
||||
<li>.feature files</li>
|
||||
<li>Given/When/Then syntax</li>
|
||||
<li>Human readable specs</li>
|
||||
<li>Single source of truth</li>
|
||||
<li>Maps to both test types</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pipeline-step tests">
|
||||
<h3><span class="num">3</span> Tests</h3>
|
||||
<ul>
|
||||
<li><strong>Backend:</strong> API contracts</li>
|
||||
<li><strong>Backend:</strong> Workflows (compositions)</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> Ops Templates</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Template Structure</h4>
|
||||
<pre>
|
||||
<span class="keyword">### [Flow Name]</span>
|
||||
|
||||
<span class="comment">User type:</span> Pet Owner / Vet / Admin
|
||||
<span class="comment">Entry point:</span> Page/button/link
|
||||
<span class="comment">Goal:</span> One sentence
|
||||
|
||||
<span class="keyword">Steps:</span>
|
||||
1. First action
|
||||
2. Second action
|
||||
3. ...
|
||||
|
||||
<span class="keyword">Expected result:</span>
|
||||
- What should happen
|
||||
|
||||
<span class="keyword">Common problems:</span>
|
||||
- Problem 1
|
||||
|
||||
<span class="keyword">Edge cases:</span>
|
||||
- Special case 1
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Source</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Template:</strong> album/template/ops-flow/</li>
|
||||
<li><strong>Reference:</strong> def/work_plan/21-plantilla</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Who Fills This</h4>
|
||||
<ul class="flow-list">
|
||||
<li>Support team (daily user contact)</li>
|
||||
<li>Ops team (knows workarounds)</li>
|
||||
<li>Product (requirements)</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Output</h4>
|
||||
<ul class="flow-list">
|
||||
<li>One .md per flow</li>
|
||||
<li>Organized by user type</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>.feature File</h4>
|
||||
<pre>
|
||||
<span class="keyword">Feature:</span> Turnero - Book appointment
|
||||
|
||||
<span class="keyword">Scenario:</span> Book vaccination for cat
|
||||
<span class="decorator">Given</span> I am on the turnero page
|
||||
<span class="decorator">When</span> I enter address <span class="string">"Av Santa Fe 1234"</span>
|
||||
<span class="decorator">And</span> I click <span class="string">"Next"</span>
|
||||
<span class="decorator">Then</span> a guest user should be created
|
||||
|
||||
<span class="decorator">When</span> I add pet <span class="string">"Koshka"</span> type <span class="string">"Cat"</span>
|
||||
<span class="decorator">And</span> I select <span class="string">"Vaccination"</span>
|
||||
<span class="decorator">Then</span> <span class="string">"Clinical consult"</span> is auto-added
|
||||
|
||||
<span class="keyword">Scenario:</span> Services filtered by pet type
|
||||
<span class="decorator">Given</span> I added a cat
|
||||
<span class="decorator">Then</span> I see cat vaccines
|
||||
<span class="decorator">And</span> I dont see dog vaccines
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Keywords</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Feature</strong> = one capability</li>
|
||||
<li><strong>Scenario</strong> = one behavior</li>
|
||||
<li><strong>Given</strong> = precondition</li>
|
||||
<li><strong>When</strong> = action</li>
|
||||
<li><strong>Then</strong> = expected result</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">Reference</h4>
|
||||
<ul class="flow-list">
|
||||
<li>def/work_plan/10-flow-turnero.md</li>
|
||||
<li>Full example with Gherkin, API tests, Page Objects</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="4">
|
||||
<h2><span class="badge bdd">2b</span> Gherkin File Organization</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Correct: One Feature = One File</h4>
|
||||
<pre>
|
||||
<span class="keyword">features/</span>
|
||||
├── pet-owner/
|
||||
│ ├── registro.feature <span class="comment"># 6-8 scenarios</span>
|
||||
│ ├── reservar-turno.feature <span class="comment"># 10-15 scenarios</span>
|
||||
│ ├── gestion-mascotas.feature
|
||||
│ └── pago.feature
|
||||
├── veterinarian/
|
||||
│ └── ...
|
||||
└── backoffice/
|
||||
└── ...
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Anti-pattern: One Scenario = One File</h4>
|
||||
<pre style="border-left: 3px solid #ef4444;">
|
||||
<span class="comment"># DON'T do this</span>
|
||||
features/pet-owner/registro/
|
||||
├── registro-exitoso.feature
|
||||
├── registro-email-invalido.feature
|
||||
├── registro-password-corto.feature
|
||||
└── <span class="comment">... (dozens of tiny files)</span>
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Why Multiple Scenarios per File</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Feature = Capability</strong> - one file describes one capability with all its behaviors</li>
|
||||
<li><strong>Context stays together</strong> - Background, Rules share context</li>
|
||||
<li><strong>Tooling expects it</strong> - test runners, reports, IDE navigation</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1.5rem;">When to Split</h4>
|
||||
<pre>
|
||||
<span class="comment"># Scenarios per file:</span>
|
||||
5-20 <span class="string">Normal, keep as is</span>
|
||||
20-40 <span class="string">Consider splitting</span>
|
||||
40+ <span class="string">Definitely split</span>
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Folder Depth</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>Good:</strong> 1-2 levels max</li>
|
||||
<li><strong>Avoid:</strong> deep nesting</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="5">
|
||||
<h2><span class="badge tests">3a</span> Backend Tests</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Structure (amar_django_back)</h4>
|
||||
<pre>
|
||||
tests/contracts/
|
||||
├── base.py <span class="comment"># mode switcher</span>
|
||||
├── endpoints.py <span class="comment"># API paths (single source)</span>
|
||||
├── helpers.py <span class="comment"># test data</span>
|
||||
│
|
||||
├── mascotas/ <span class="comment"># app tests</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"># compositions</span>
|
||||
└── test_turnero_general.py
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Two Test Modes</h4>
|
||||
<pre>
|
||||
<span class="comment"># Fast (Django test client)</span>
|
||||
pytest tests/contracts/
|
||||
|
||||
<span class="comment"># Live (real HTTP)</span>
|
||||
CONTRACT_TEST_MODE=live pytest
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Workflow = Composition</h4>
|
||||
<pre>
|
||||
<span class="comment"># Calls endpoints in sequence:</span>
|
||||
1. Check coverage
|
||||
2. Create pet owner
|
||||
3. Create pet
|
||||
4. Get services
|
||||
5. Create request
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Key Files</h4>
|
||||
<ul class="flow-list">
|
||||
<li><strong>endpoints.py</strong> - change paths here only</li>
|
||||
<li><strong>helpers.py</strong> - sample data</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="6">
|
||||
<h2><span class="badge tests">3b</span> Frontend Tests</h2>
|
||||
<div class="two-col">
|
||||
<div class="col">
|
||||
<h4>Structure (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"># E2E test</span>
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Page Object Pattern</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>Running Tests</h4>
|
||||
<pre>
|
||||
<span class="comment"># All tests</span>
|
||||
npx playwright test
|
||||
|
||||
<span class="comment"># With UI</span>
|
||||
npx playwright test --ui
|
||||
|
||||
<span class="comment"># Specific file</span>
|
||||
npx playwright test login.spec.ts
|
||||
</pre>
|
||||
<h4 style="margin-top: 1rem;">Locator Priority</h4>
|
||||
<ul class="flow-list">
|
||||
<li>1. getByRole() - buttons, links</li>
|
||||
<li>2. getByLabel() - form fields</li>
|
||||
<li>3. getByText() - visible text</li>
|
||||
<li>4. getByTestId() - data-testid</li>
|
||||
</ul>
|
||||
<h4 style="margin-top: 1rem;">Avoid</h4>
|
||||
<ul class="flow-list">
|
||||
<li>CSS class selectors</li>
|
||||
<li>Complex XPath</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-slide="7">
|
||||
<h2>Per-Feature Checklist</h2>
|
||||
<ul class="checklist">
|
||||
<li><span class="check"></span> Ops template filled (support team)</li>
|
||||
<li><span class="check"></span> Convert to .feature file (Gherkin spec)</li>
|
||||
<li><span class="check"></span> Backend: API contract tests per endpoint</li>
|
||||
<li><span class="check"></span> Backend: Workflow test (composition)</li>
|
||||
<li><span class="check"></span> Frontend: Page Object (if new page)</li>
|
||||
<li><span class="check"></span> Frontend: E2E spec (Playwright)</li>
|
||||
<li><span class="check"></span> Wire to CI</li>
|
||||
</ul>
|
||||
<div style="margin-top: 2rem; color: #64748b; font-size: 0.9rem;">
|
||||
<p><strong>Full example:</strong> def/work_plan/10-flow-turnero.md</p>
|
||||
<p><strong>Backend README:</strong> amar_django_back/tests/contracts/README.md</p>
|
||||
<p><strong>Frontend README:</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>
|
||||
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