en el medio del kilombo de la db
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -14,6 +14,11 @@ supabase/.temp/
|
|||||||
# Node
|
# Node
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
# Playwright — reportes/snapshots, pueden contener credenciales de .env.test en el DOM
|
||||||
|
test-results/
|
||||||
|
playwright-report/
|
||||||
|
blob-report/
|
||||||
|
|
||||||
# Next.js build output
|
# Next.js build output
|
||||||
.next/
|
.next/
|
||||||
|
|
||||||
|
|||||||
63
package-lock.json
generated
63
package-lock.json
generated
@@ -19,6 +19,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@axe-core/cli": "^4.11.3",
|
"@axe-core/cli": "^4.11.3",
|
||||||
|
"@playwright/test": "^1.61.1",
|
||||||
"@tailwindcss/postcss": "^4.1.8",
|
"@tailwindcss/postcss": "^4.1.8",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
@@ -1041,6 +1042,22 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@playwright/test": {
|
||||||
|
"version": "1.61.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz",
|
||||||
|
"integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "1.61.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@standard-schema/utils": {
|
"node_modules/@standard-schema/utils": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
||||||
@@ -2135,6 +2152,20 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
@@ -3002,6 +3033,38 @@
|
|||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.61.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz",
|
||||||
|
"integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.61.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.61.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz",
|
||||||
|
"integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.15",
|
"version": "8.5.15",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@axe-core/cli": "^4.11.3",
|
"@axe-core/cli": "^4.11.3",
|
||||||
|
"@playwright/test": "^1.61.1",
|
||||||
"@tailwindcss/postcss": "^4.1.8",
|
"@tailwindcss/postcss": "^4.1.8",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
|
|||||||
14
playwright.config.ts
Normal file
14
playwright.config.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { defineConfig } from '@playwright/test'
|
||||||
|
|
||||||
|
// .env.test nunca se commitea (ver .gitignore) — credenciales de un usuario
|
||||||
|
// dedicado de e2e, nunca las del director.
|
||||||
|
process.loadEnvFile('.env.test')
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: 'tests/e2e',
|
||||||
|
use: {
|
||||||
|
baseURL: process.env.E2E_BASE_URL,
|
||||||
|
},
|
||||||
|
// Local contra producción — no corre en CI (backlog).
|
||||||
|
reporter: 'list',
|
||||||
|
})
|
||||||
154
sprints/sprint-5/ETAPA_ADMIN_PROMPT.md
Normal file
154
sprints/sprint-5/ETAPA_ADMIN_PROMPT.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# ETAPA ADMIN — Vista de respuestas mejorada + vista detallada
|
||||||
|
|
||||||
|
## 1. Objetivo
|
||||||
|
Mejorar `/admin/responses` para que sea una interfaz de administración real,
|
||||||
|
y crear `/admin/responses/[id]` como vista detallada de una respuesta individual.
|
||||||
|
Todo detrás del middleware de auth existente.
|
||||||
|
|
||||||
|
## 2. Contexto
|
||||||
|
- Estado actual: `app/admin/responses/page.tsx` (Server Component, 249 líneas) +
|
||||||
|
`app/admin/responses/responses-table.tsx` (Client Component, 98 líneas).
|
||||||
|
- Auth: ya implementada — getSupabaseSession() con anon key + cookies, respeta RLS.
|
||||||
|
- Exclusión de sensibles: `is_sensitive=false` y `type != 'text_long'` en query de
|
||||||
|
questions — NO tocar, mantener exactamente igual.
|
||||||
|
- SURVEY_OPTIONS hardcodeadas con UUIDs literales — reemplazar por query dinámica.
|
||||||
|
- LIMIT 500 sin paginación real — reemplazar por paginación con page size de 50.
|
||||||
|
|
||||||
|
## 3. DECISIONES YA TOMADAS — no consultar
|
||||||
|
- Paginación: page size 50, parámetro `?page=N` en la URL (Server Component,
|
||||||
|
recarga completa — no client-side infinite scroll).
|
||||||
|
- Filtro de survey: `<select>` que carga surveys desde la DB (solo las que el
|
||||||
|
usuario puede ver según RLS), no hardcodeado.
|
||||||
|
- Vista detallada: ruta `/admin/responses/[id]` — Server Component, muestra todas
|
||||||
|
las respuestas no sensibles de esa submission, agrupadas por sección.
|
||||||
|
- Diseño: usar clases Tailwind o clases `.admin-*` existentes — sin nuevo sistema.
|
||||||
|
- Sin librería de tablas externa.
|
||||||
|
- El expand/collapse por submission en la tabla se conserva.
|
||||||
|
|
||||||
|
## 4. Tareas
|
||||||
|
|
||||||
|
### 4.1 Vista lista mejorada — `app/admin/responses/page.tsx`
|
||||||
|
|
||||||
|
**Filtro de survey dinámico:**
|
||||||
|
Reemplazar SURVEY_OPTIONS hardcodeadas por query a `surveys` table:
|
||||||
|
```ts
|
||||||
|
const { data: surveys } = await supabase
|
||||||
|
.from('surveys')
|
||||||
|
.select('id, title')
|
||||||
|
.order('title')
|
||||||
|
```
|
||||||
|
Renderizar como `<select>` con onChange que navega a `?survey_id=<id>`.
|
||||||
|
Opción "Todos los surveys" con value=''.
|
||||||
|
|
||||||
|
**Paginación:**
|
||||||
|
- Leer `searchParams.page` (default 1).
|
||||||
|
- Query de responses: `.range((page-1)*50, page*50-1)` en vez de `.limit(500)`.
|
||||||
|
- Mostrar: "Página N de M · X respuestas totales".
|
||||||
|
- Links Anterior / Siguiente que modifican `?page=N` en la URL.
|
||||||
|
- Si hay menos de 50 resultados, no mostrar paginación.
|
||||||
|
|
||||||
|
**Columna de acciones:**
|
||||||
|
Agregar columna "Ver" en la fila cabecera de cada submission (isFirstInGroup=true)
|
||||||
|
con link a `/admin/responses/[response_id]`.
|
||||||
|
|
||||||
|
**Header informativo:**
|
||||||
|
Mostrar claramente: total de submissions, total de answers, survey seleccionado.
|
||||||
|
|
||||||
|
### 4.2 Vista detallada — `app/admin/responses/[id]/page.tsx`
|
||||||
|
|
||||||
|
Server Component. Ruta: `/admin/responses/[id]` donde `id` = `response_id` (uuid).
|
||||||
|
|
||||||
|
**Query principal:**
|
||||||
|
```ts
|
||||||
|
// 1. Metadata de la respuesta
|
||||||
|
const { data: response } = await supabase
|
||||||
|
.from('responses')
|
||||||
|
.select('id, submitted_at, qi_zone, qi_sector, qi_age_bucket, survey_id')
|
||||||
|
.eq('id', id)
|
||||||
|
.single()
|
||||||
|
|
||||||
|
// 2. Preguntas permitidas (mismo filtro que la lista)
|
||||||
|
const { data: questions } = await supabase
|
||||||
|
.from('questions')
|
||||||
|
.select('id, code, prompt, type, position, section_number, section_title')
|
||||||
|
.eq('is_sensitive', false)
|
||||||
|
.neq('type', 'text_long')
|
||||||
|
.eq('survey_id', response.survey_id)
|
||||||
|
.order('position')
|
||||||
|
|
||||||
|
// 3. Answers de esta respuesta
|
||||||
|
const { data: answers } = await supabase
|
||||||
|
.from('answers')
|
||||||
|
.select('question_id, value, instance_id')
|
||||||
|
.eq('response_id', id)
|
||||||
|
.in('question_id', questions.map(q => q.id))
|
||||||
|
```
|
||||||
|
|
||||||
|
**Layout:**
|
||||||
|
- Header: fecha, zona, sector, edad — datos de qi_* de la respuesta.
|
||||||
|
- Link "← Volver a la lista" que preserva el `?survey_id` y `?page` anteriores
|
||||||
|
(pasar como searchParam o usar `referrer`).
|
||||||
|
- Respuestas agrupadas por sección (section_number + section_title).
|
||||||
|
- Dentro de cada sección: tabla código | pregunta | valor.
|
||||||
|
- Instancias (instance_id != 'default'): mostrar agrupadas bajo "Familiar N"
|
||||||
|
con badge visual.
|
||||||
|
- Valores nulos: badge "sin respuesta" (igual que la lista).
|
||||||
|
- Si `id` no existe o RLS bloquea: mostrar "Respuesta no encontrada" —
|
||||||
|
no crashear.
|
||||||
|
|
||||||
|
### 4.3 Tabla de respuestas — `app/admin/responses/responses-table.tsx`
|
||||||
|
Agregar la columna "Ver" en las filas isFirstInGroup con `<Link>` a
|
||||||
|
`/admin/responses/[submissionId]`. Pasar `surveyId` y `page` como props
|
||||||
|
para construir el link de "← Volver" en la vista detallada.
|
||||||
|
|
||||||
|
## 5. DoD antes de reportar
|
||||||
|
- [ ] `git log --name-only -1` — listar todos los archivos tocados
|
||||||
|
- [ ] Filtro de survey carga desde DB, no hardcodeado
|
||||||
|
- [ ] Paginación funciona: page=1 muestra primeros 50, page=2 los siguientes
|
||||||
|
- [ ] Link "Ver" en cada submission lleva a `/admin/responses/[id]`
|
||||||
|
- [ ] Vista detallada muestra respuestas agrupadas por sección
|
||||||
|
- [ ] Instancias agrupadas bajo "Familiar N"
|
||||||
|
- [ ] RLS respetado: misma exclusión de sensibles que la lista
|
||||||
|
- [ ] "Respuesta no encontrada" si el id no existe
|
||||||
|
- [ ] Build sin errores: `npm run build`
|
||||||
|
- [ ] `git log --name-only -1`
|
||||||
|
- [ ] Commit + push a Gitea
|
||||||
|
|
||||||
|
## 6. Qué reportar
|
||||||
|
```
|
||||||
|
Verificado:
|
||||||
|
- Filtro survey: ✓ — query a tabla surveys, no hardcodeado
|
||||||
|
- Paginación: ✓ — page size 50, range correcto
|
||||||
|
- Link "Ver" → /admin/responses/[id]: ✓
|
||||||
|
- Vista detallada: ✓ — agrupada por sección, instancias bajo "Familiar N"
|
||||||
|
- RLS/exclusión sensibles: ✓ — mismo filtro que antes
|
||||||
|
- "No encontrada" si id inválido: ✓
|
||||||
|
- Build: ✓
|
||||||
|
- git log --name-only -1: [lista completa]
|
||||||
|
- Commit: [hash]
|
||||||
|
|
||||||
|
Decisiones de diseño tomadas:
|
||||||
|
- [lo que haya]
|
||||||
|
|
||||||
|
Hallazgos:
|
||||||
|
- [lo que aparezca, especialmente sobre questions.section_number/section_title
|
||||||
|
— si esas columnas no existen, reportarlo antes de inventar una solución]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Qué NO hacer
|
||||||
|
- ❌ No tocar el filtro `is_sensitive=false` ni `type != 'text_long'`
|
||||||
|
- ❌ No usar service_role — solo getSupabaseSession() con RLS
|
||||||
|
- ❌ No agregar librerías de tablas o UI externas
|
||||||
|
- ❌ No implementar búsqueda por texto (fuera de alcance)
|
||||||
|
- ❌ No hacer deploy — solo commit y push a Gitea
|
||||||
|
- ❌ Si `section_number` o `section_title` no existen en questions,
|
||||||
|
reportarlo y NO inventar columnas — esperar instrucción
|
||||||
|
|
||||||
|
## 8. Versionado
|
||||||
|
```
|
||||||
|
feat(admin): vista respuestas mejorada + vista detallada [admin]
|
||||||
|
|
||||||
|
/admin/responses: filtro survey dinámico, paginación 50/página,
|
||||||
|
link a vista detallada. /admin/responses/[id]: respuestas por sección,
|
||||||
|
instancias bajo "Familiar N". RLS y exclusión de sensibles intactos.
|
||||||
|
```
|
||||||
89
sprints/sprint-5/ETAPA_E2E_PROMPT.md
Normal file
89
sprints/sprint-5/ETAPA_E2E_PROMPT.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# ETAPA E2E — Tests Playwright: verificación deploy Sprint 5
|
||||||
|
|
||||||
|
## Antes de tocar código, leé:
|
||||||
|
1. `CLAUDE.md`
|
||||||
|
2. `docs/estado/ESTADO_PROYECTO_26JUN2026.md` — estado general del proyecto
|
||||||
|
|
||||||
|
Confirmame el plan antes de implementar.
|
||||||
|
|
||||||
|
## Credenciales de test (usuario dedicado — nunca las de Marcos)
|
||||||
|
- URL: `https://economia-cuidado.inqualityhq.com`
|
||||||
|
- Email: `test-e2e@inquality.com.py`
|
||||||
|
- Password: `TestE2E2026!#Cuidado`
|
||||||
|
- Rol: `platform_admin`, org: `InQuality`
|
||||||
|
|
||||||
|
Estas credenciales van en `.env.test` en la raíz (agregar a `.gitignore` si no está).
|
||||||
|
**Nunca hardcodeadas en los specs.**
|
||||||
|
|
||||||
|
## Verificar Playwright antes de instalar nada
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright --version 2>/dev/null && echo "instalado" || echo "no instalado"
|
||||||
|
ls ~/.cache/ms-playwright 2>/dev/null && echo "browsers presentes" || echo "browsers ausentes"
|
||||||
|
```
|
||||||
|
|
||||||
|
Reportar resultado antes de continuar. Si ya está instalado con browsers, agregar solo
|
||||||
|
`@playwright/test` como devDependency sin correr `npx playwright install`.
|
||||||
|
Si no está: `npx playwright install --with-deps chromium` (solo Chromium).
|
||||||
|
|
||||||
|
## Los 5 tests a implementar
|
||||||
|
|
||||||
|
**Archivo:** `tests/e2e/sprint5-verificacion.spec.ts`
|
||||||
|
|
||||||
|
### Test 1 — Login y redirect
|
||||||
|
- Abrir `/admin/responses` sin sesión
|
||||||
|
- Verificar redirect a `/login`
|
||||||
|
- El DOM de `/login` contiene el campo `email`, el campo `password`, y el botón de toggle
|
||||||
|
de contraseña (buscar `aria-label` que contenga "contraseña")
|
||||||
|
|
||||||
|
### Test 2 — Login funcional
|
||||||
|
- Hacer login con las credenciales de test en `/login`
|
||||||
|
- Verificar redirect a `/admin/responses`
|
||||||
|
- El DOM contiene `<select>` para filtro de survey (no links hardcodeados)
|
||||||
|
- El DOM contiene al menos una fila de respuesta (hay 50 sembradas)
|
||||||
|
|
||||||
|
### Test 3 — Vista detallada de respuesta
|
||||||
|
- Desde `/admin/responses`, hacer click en el primer link "Ver" disponible
|
||||||
|
- Verificar que la URL cambia a `/admin/responses/[uuid]`
|
||||||
|
- El DOM contiene al menos una sección (buscar texto que indique agrupación por sección)
|
||||||
|
- No hay error 404 ni texto "Respuesta no encontrada"
|
||||||
|
|
||||||
|
### Test 4 — Dashboard con datos
|
||||||
|
- Navegar a `/dashboard` (o la ruta del dashboard — verificar cuál es)
|
||||||
|
- Verificar que al menos un widget muestra un porcentaje (buscar `%` en el DOM)
|
||||||
|
- No aparece "Datos insuficientes" en todos los widgets (puede aparecer en algunos
|
||||||
|
segmentos con k<5, pero no en todos)
|
||||||
|
|
||||||
|
### Test 5 — Formulario público accesible sin auth
|
||||||
|
- Navegar a `/` o `/encuesta` (verificar ruta correcta del formulario)
|
||||||
|
- Verificar que NO redirige a `/login` (el formulario es público)
|
||||||
|
- El DOM contiene el wizard o la pantalla de consentimiento
|
||||||
|
|
||||||
|
## Decisiones ya tomadas
|
||||||
|
- Playwright en local apuntando a producción, no en CI (backlog)
|
||||||
|
- `playwright.config.ts` en raíz, `testDir: 'tests/e2e'`
|
||||||
|
- `.env.test` para credenciales, en `.gitignore`
|
||||||
|
- Si un test falla, reportar el error exacto y consultarme — no intentar workarounds
|
||||||
|
|
||||||
|
## Archivos autorizados
|
||||||
|
`playwright.config.ts`, `tests/e2e/sprint5-verificacion.spec.ts`, `.env.test`
|
||||||
|
(no se commitea), `package.json` (devDependency), `package-lock.json`.
|
||||||
|
Cualquier otro en `git diff --name-only HEAD` → revertir y reportar.
|
||||||
|
|
||||||
|
## DoD antes de reportar
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright test tests/e2e/sprint5-verificacion.spec.ts --reporter=list
|
||||||
|
git log --name-only -1
|
||||||
|
git diff --name-only HEAD # solo archivos autorizados, sin .env.test
|
||||||
|
grep -r "TestE2E2026" . # debe dar vacío — contraseña no commiteada
|
||||||
|
```
|
||||||
|
|
||||||
|
## Qué reportar
|
||||||
|
- Resultado de checks de Playwright previos (versión, browsers)
|
||||||
|
- Resultado de cada test: pass/fail con mensaje exacto si falla
|
||||||
|
- Ruta real del dashboard y del formulario público (las que encontró en el código)
|
||||||
|
- `git log --name-only -1` y `git diff --name-only HEAD`
|
||||||
|
- Hallazgos no esperados
|
||||||
|
|
||||||
|
**No hacer deploy ni modificar nada en producción. No continuar sin mi OK si algo falla.**
|
||||||
70
tests/e2e/sprint5-verificacion.spec.ts
Normal file
70
tests/e2e/sprint5-verificacion.spec.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { test, expect, type Page, type BrowserContext } from '@playwright/test'
|
||||||
|
|
||||||
|
// Credenciales SIEMPRE desde .env.test (playwright.config.ts las carga) — nunca acá.
|
||||||
|
const EMAIL = process.env.E2E_EMAIL!
|
||||||
|
const PASSWORD = process.env.E2E_PASSWORD!
|
||||||
|
|
||||||
|
// Survey Demo — la única con datos sembrados (51 respuestas, ver
|
||||||
|
// docs/estado/ESTADO_PROYECTO_26JUN2026.md §4). Necesaria como ?s= en el
|
||||||
|
// formulario público: con 3 surveys en `live`, `/` sin parámetro es ambiguo
|
||||||
|
// (app/page.tsx usa .maybeSingle() sobre status='live').
|
||||||
|
const DEMO_SURVEY_ID = '30000000-0000-0000-0000-000000000001'
|
||||||
|
|
||||||
|
test.describe.serial('Sprint 5 — verificación post-deploy', () => {
|
||||||
|
let context: BrowserContext
|
||||||
|
let page: Page
|
||||||
|
|
||||||
|
test.beforeAll(async ({ browser }) => {
|
||||||
|
context = await browser.newContext()
|
||||||
|
page = await context.newPage()
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterAll(async () => {
|
||||||
|
await context.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('1. /admin/responses sin sesión redirige a /login', async () => {
|
||||||
|
await page.goto('/admin/responses')
|
||||||
|
await expect(page).toHaveURL(/\/login/)
|
||||||
|
await expect(page.getByRole('textbox', { name: 'Email' })).toBeVisible()
|
||||||
|
await expect(page.getByRole('textbox', { name: 'Contraseña' })).toBeVisible()
|
||||||
|
await expect(page.getByRole('button', { name: /contraseña/i })).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('2. login funcional redirige a /admin/responses con filtro y datos', async () => {
|
||||||
|
await page.getByRole('textbox', { name: 'Email' }).fill(EMAIL)
|
||||||
|
await page.getByRole('textbox', { name: 'Contraseña' }).fill(PASSWORD)
|
||||||
|
await page.getByRole('button', { name: 'Ingresar' }).click()
|
||||||
|
await expect(page).toHaveURL(/\/admin\/responses/)
|
||||||
|
await expect(page.locator('select.admin-survey-select')).toBeVisible()
|
||||||
|
await expect(page.locator('table.admin-table tbody tr').first()).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('3. vista detallada de una respuesta', async () => {
|
||||||
|
await page.locator('a.admin-action-link', { hasText: 'Ver' }).first().click()
|
||||||
|
await expect(page).toHaveURL(/\/admin\/responses\/[0-9a-f-]{36}/)
|
||||||
|
await expect(page.locator('.admin-detail-section').first()).toBeVisible()
|
||||||
|
await expect(page.getByText('Respuesta no encontrada')).toHaveCount(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('4. dashboard muestra datos reales (no todo suprimido por k<5)', async () => {
|
||||||
|
await page.goto('/dashboard')
|
||||||
|
await expect(page.locator('.db-widget').first()).toBeVisible()
|
||||||
|
await expect(page.locator('body')).toContainText('%')
|
||||||
|
const totalWidgets = await page.locator('.db-widget').count()
|
||||||
|
const suppressed = await page.locator('.db-suppressed').count()
|
||||||
|
expect(totalWidgets).toBeGreaterThan(0)
|
||||||
|
expect(suppressed).toBeLessThan(totalWidgets)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('5. formulario público accesible sin auth', async ({ browser }) => {
|
||||||
|
const anonContext = await browser.newContext()
|
||||||
|
const anonPage = await anonContext.newPage()
|
||||||
|
await anonPage.goto(`/?s=${DEMO_SURVEY_ID}`)
|
||||||
|
await expect(anonPage).not.toHaveURL(/\/login/)
|
||||||
|
await expect(
|
||||||
|
anonPage.locator('.consent-container, .wizard-shell').first()
|
||||||
|
).toBeVisible()
|
||||||
|
await anonContext.close()
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user