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>
This commit is contained in:
135
tests/e2e/etapa-9-badge.spec.ts
Normal file
135
tests/e2e/etapa-9-badge.spec.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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()
|
||||
})
|
||||
Reference in New Issue
Block a user