`municipality.engagementLevel` (staff-only, no default) records what the campaign decided to invest in a município, next to the E8/E10 metrics that describe what it knows. Moving it is a reallocation decision, so only unrestricted staff may, every move carries a rationale plus the signals that would reverse it, and the write files an `allocationDecision` in the same transaction under an advisory lock taken before the current level is read — otherwise the snapshot's `from` is a guess. The enum gains `movimento`: a level move is neither `aceita` nor `descarta`. No backfill. Null is "Sem nível", and filtering by it is the triage queue. That is also why the list sorts `nivel` in memory rather than in the query: Postgres puts NULLs first on DESC, so a native sort would open "N4 primeiro" on a page of municípios nobody has decided about yet. The filter stays native. The hysteresis rules from research §6.8 are pure and client-safe, so the Popover explains a block before submitting and the server re-runs them anyway; a blocked move answers 409 with typed violations and the override is recorded with exactly the rules it contradicted.
37 lines
1.3 KiB
TypeScript
37 lines
1.3 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
|
|
const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:3000'
|
|
|
|
test('prewarms shared Next route bundles sequentially', async ({ request }) => {
|
|
test.slow()
|
|
for (const path of [
|
|
'/campanha/login',
|
|
'/campanha',
|
|
'/campanha/municipios',
|
|
'/campanha/territorios',
|
|
'/campanha/municipios/e2e-prewarm',
|
|
'/campanha/demandas',
|
|
'/campanha/liderancas',
|
|
'/campanha/conceitos',
|
|
'/campanha/convite/e2e-prewarm',
|
|
'/',
|
|
]) {
|
|
const response = await request.get(`${baseURL}${path}`)
|
|
expect(response.ok(), `Failed to prewarm ${path}`).toBe(true)
|
|
}
|
|
|
|
// POST-only API route handlers (auto-save popovers): Next dev compiles a
|
|
// route on its first hit, and that compile can trigger a full-page reload
|
|
// for any client currently connected — which aborts an in-flight fetch mid
|
|
// test. An unauthenticated POST never succeeds, but it still forces the
|
|
// compile before any spec's client makes the real request.
|
|
for (const path of [
|
|
'/campanha/municipios/advisors',
|
|
'/campanha/municipios/engagement-level',
|
|
'/campanha/municipios/expected-votes',
|
|
'/campanha/municipios/political-trend',
|
|
'/campanha/liderancas/support-status',
|
|
]) {
|
|
await request.post(`${baseURL}${path}`, { data: {} }).catch(() => undefined)
|
|
}
|
|
})
|