qa-analyzer: plan QA por HU generado por IA + indicadores
- services/qa-analyzer.ts: genera plan QA (tests, automatizable/manual, pasos) - db.ts v5: tabla qa_plans - DashboardView: push HU/épica auto-genera QA plan - DashboardView: stats card QA con conteo de planes - DashboardView: boton QA por draft + visualización de planes - project-analyzer: saveAsDrafts actualizado con metadata para épicas
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { callAI } from '@/services/ai'
|
||||
import db from '@/services/db'
|
||||
|
||||
export interface QATestCase {
|
||||
type: string
|
||||
description: string
|
||||
automatable: 'SÍ' | 'PARCIAL' | 'MANUAL'
|
||||
tool: string
|
||||
}
|
||||
|
||||
export interface HUQAPlan {
|
||||
huTitle: string
|
||||
huId: string
|
||||
acceptanceCriteria: string[]
|
||||
testCases: QATestCase[]
|
||||
manualSteps: string[]
|
||||
criticalTests: string[]
|
||||
}
|
||||
|
||||
const QA_PROMPT = `Eres un ingeniero de QA experto. Generá un plan de pruebas detallado para una Historia de Usuario.
|
||||
|
||||
Formato de respuesta JSON:
|
||||
{
|
||||
"acceptanceCriteria": ["Criterio 1", "Criterio 2"],
|
||||
"testCases": [
|
||||
{
|
||||
"type": "Tipo de prueba",
|
||||
"description": "Descripción de lo que verifica",
|
||||
"automatizable": "SÍ|PARCIAL|MANUAL",
|
||||
"tool": "Playwright|API Testing|Manual"
|
||||
}
|
||||
],
|
||||
"manualSteps": ["Paso manual 1"],
|
||||
"criticalTests": ["Prueba crítica manual"]
|
||||
}
|
||||
|
||||
Reglas:
|
||||
- SÍ = completamente automatizable
|
||||
- PARCIAL = requiere validación manual complementaria
|
||||
- MANUAL = requiere intervención humana
|
||||
- Incluí al menos 3-5 casos de prueba
|
||||
- Marcá como crítica pruebas con datos reales, ERPs externos, o cálculos financieros`
|
||||
|
||||
export async function generateQAPlan(huTitle: string, huDescription: string, acceptanceCriteria: string): Promise<HUQAPlan> {
|
||||
const userContent = `HU: ${huTitle}\nDescripción: ${huDescription}\nCriterios: ${acceptanceCriteria}`
|
||||
const content = await callAI(
|
||||
[{ role: 'system', content: QA_PROMPT }, { role: 'user', content: userContent }],
|
||||
0.3, 4096,
|
||||
)
|
||||
try {
|
||||
const jsonStr = extractJSON(content)
|
||||
const result: Omit<HUQAPlan, 'huTitle' | 'huId'> = JSON.parse(jsonStr)
|
||||
return { huTitle, huId: '', ...result }
|
||||
} catch (e) {
|
||||
console.error('[Alpha] Failed to parse QA plan. Raw:', content)
|
||||
throw new Error(`Error generando plan QA para: ${huTitle}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveQAPlan(projectId: number, huId: string, huTitle: string, plan: HUQAPlan) {
|
||||
await db.table('qa_plans').put({
|
||||
id: `${projectId}-${huId}`, projectId, huId, huTitle,
|
||||
plan: JSON.stringify(plan),
|
||||
createdAt: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
export async function getQAPlans(projectId: number) {
|
||||
return db.table('qa_plans').where('projectId').equals(projectId).toArray()
|
||||
}
|
||||
|
||||
export async function generateAndSavePlan(projectId: number, huId: string, huTitle: string, huDescription: string, acceptanceCriteria: string): Promise<HUQAPlan> {
|
||||
const plan = await generateQAPlan(huTitle, huDescription, acceptanceCriteria)
|
||||
plan.huId = huId
|
||||
await saveQAPlan(projectId, huId, huTitle, plan)
|
||||
return plan
|
||||
}
|
||||
|
||||
function extractJSON(text: string): string {
|
||||
try { JSON.parse(text); return text } catch {}
|
||||
const block = text.match(/```(?:json)?\s*([\s\S]*?)```/)
|
||||
if (block) { try { JSON.parse(block[1].trim()); return block[1].trim() } catch {} }
|
||||
const first = text.indexOf('{')
|
||||
const last = text.lastIndexOf('}')
|
||||
if (first !== -1 && last > first) { const c = text.slice(first, last + 1); try { JSON.parse(c); return c } catch {} }
|
||||
return text
|
||||
}
|
||||
Reference in New Issue
Block a user