Files
inq-roi-simulador-web/tests/e2e/etapa-9-badge.spec.ts
Marcos Benítez 3834c9f1c1 feat(sprint-1.5): PDFs Actual/Auto con identidad InQ + badge naranja (Etapas 8-9)
Etapa 8 — PDFs Actual y Automatizado:
- Página 1: header denso + metadata strip + grid 2×2 KPI cards con paleta
  #FEF3E2 (naranja claro InQ), sin gradiente (jerarquía visual preservada)
- Página 3: tabla con header naranja claro #FFF8ED + #92400E, em dash en
  filas no-automatizables, título ANÁLISIS DE COMPOSICIÓN uppercase
- Página 4: NOTA METODOLÓGICA uppercase + caja contenedora slate-50
- 526 tests verdes (+ 15 tests nuevos)

Etapa 9 — Badge  en BpmnCanvas:
- Reemplaza badge legacy (Bot SVG ámbar #fffbeb) por círculo naranja
  sólido #F59845 con símbolo  blanco, 14×14px, border white 1.5px
- Usa var(--inq-orange) definido en globals.css (sin hex hardcodeados)
- Elimina los 3 hex legacy del audit Etapa 1: #f59e0b, #fffbeb, #e2e8f0
- 5 tests E2E de lifecycle del badge + 2 screenshots

Política de iniciativa refinada post-Sprint 1.5 (CLAUDE.md actualizado)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 10:03:37 -03:00

136 lines
5.1 KiB
TypeScript

/**
* Etapa 9 Sprint 1.5 — Badge ⚡ naranja InQ en BpmnCanvas.
*
* Validaciones:
* 1. El badge aparece cuando una actividad se marca como automatizable
* 2. El badge desaparece cuando se desmarca (toggle off)
* 3. El badge reaparece al volver a marcar (toggle on)
* 4. El badge usa var(--inq-orange) / naranja InQ (no ámbar legacy)
* 5. Screenshots: workspace-badge-on.png + workspace-badge-detail.png
*/
import { test, expect } from '@playwright/test'
import { resolve } from 'path'
import { mkdirSync } from 'fs'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const BPMN_DIR = resolve(__dirname, '../../public/sample-processes')
const SCREENSHOTS_DIR = resolve(__dirname, '../screenshots/etapa-9')
test.beforeAll(() => { mkdirSync(SCREENSHOTS_DIR, { recursive: true }) })
async function loadProcess(page: import('@playwright/test').Page, filename: string) {
await page.goto('/')
await page.locator('#bpmn-file-input').setInputFiles(resolve(BPMN_DIR, filename))
await page.waitForURL(/\/workspace\//, { timeout: 15_000 })
await page.waitForSelector('button:has-text("Simular")', { timeout: 10_000 })
await page.waitForTimeout(800)
}
async function markAutomatable(page: import('@playwright/test').Page, elementId: string) {
await page.locator(`[data-element-id="${elementId}"]`).first().click()
await page.waitForTimeout(400)
const toggle = page.getByRole('switch', { name: /automatizable/i })
const checked = await toggle.getAttribute('aria-checked')
if (checked === 'false') {
await toggle.click()
await page.waitForTimeout(200)
// Guardar es necesario para persistir al store → actualiza automatableElementIds
await page.getByRole('button', { name: /Guardar/i }).click()
}
await page.waitForSelector('.automatable-badge', { timeout: 8_000 })
}
async function unmarkAutomatable(page: import('@playwright/test').Page, elementId: string) {
await page.locator(`[data-element-id="${elementId}"]`).first().click()
await page.waitForTimeout(400)
const toggle = page.getByRole('switch', { name: /automatizable/i })
const checked = await toggle.getAttribute('aria-checked')
if (checked === 'true') {
await toggle.click()
await page.waitForTimeout(200)
await page.getByRole('button', { name: /Guardar/i }).click()
}
await page.waitForTimeout(800) // esperar que el overlay se elimine
}
test('badge ⚡ aparece al marcar actividad como automatizable', async ({ page }) => {
await loadProcess(page, 'medium-with-gateways.bpmn')
await markAutomatable(page, 'task_recv')
const badge = page.locator('.automatable-badge').first()
await expect(badge).toBeVisible({ timeout: 5_000 })
await expect(badge).toHaveText('⚡')
})
test('badge ⚡ desaparece al desmarcar actividad', async ({ page }) => {
await loadProcess(page, 'medium-with-gateways.bpmn')
// Marcar
await markAutomatable(page, 'task_recv')
await expect(page.locator('.automatable-badge').first()).toBeVisible({ timeout: 5_000 })
// Desmarcar
await unmarkAutomatable(page, 'task_recv')
await expect(page.locator('.automatable-badge')).toHaveCount(0, { timeout: 5_000 })
})
test('badge ⚡ reaparece después de toggle on → off → on', async ({ page }) => {
await loadProcess(page, 'medium-with-gateways.bpmn')
await markAutomatable(page, 'task_recv')
await expect(page.locator('.automatable-badge')).toHaveCount(1, { timeout: 5_000 })
await unmarkAutomatable(page, 'task_recv')
await expect(page.locator('.automatable-badge')).toHaveCount(0, { timeout: 5_000 })
await markAutomatable(page, 'task_recv')
await expect(page.locator('.automatable-badge')).toHaveCount(1, { timeout: 5_000 })
})
test('screenshot workspace-badge-on: múltiples badges visibles', async ({ page }) => {
await loadProcess(page, 'medium-with-gateways.bpmn')
for (const id of ['task_recv', 'task_score', 'task_analisis']) {
const el = page.locator(`[data-element-id="${id}"]`)
if (await el.count() > 0) {
await markAutomatable(page, id)
}
}
await page.waitForTimeout(400)
await page.screenshot({
path: resolve(SCREENSHOTS_DIR, 'workspace-badge-on.png'),
fullPage: false,
})
const badgeCount = await page.locator('.automatable-badge').count()
expect(badgeCount).toBeGreaterThanOrEqual(1)
})
test('screenshot workspace-badge-detail: zoom in sobre nodo automatizable', async ({ page }) => {
await loadProcess(page, 'medium-with-gateways.bpmn')
await markAutomatable(page, 'task_recv')
// Capturar área del nodo con el badge
const nodeWithBadge = page.locator('[data-element-id="task_recv"]').first()
const box = await nodeWithBadge.boundingBox()
if (box) {
const padding = 40
await page.screenshot({
path: resolve(SCREENSHOTS_DIR, 'workspace-badge-detail.png'),
clip: {
x: Math.max(0, box.x - padding),
y: Math.max(0, box.y - padding),
width: box.width + padding * 2,
height: box.height + padding * 2,
},
})
} else {
await page.screenshot({ path: resolve(SCREENSHOTS_DIR, 'workspace-badge-detail.png') })
}
await expect(page.locator('.automatable-badge')).toBeVisible()
})