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>
136 lines
5.1 KiB
TypeScript
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()
|
|
})
|