From a8cb77130318aff8efe936bfb3064e8c54c13f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Wed, 29 Jul 2026 23:33:46 -0300 Subject: [PATCH] B62+: flatten adversary signal to boolean and harden leader redirect e2e. The simplify pass dropped the vestigial object wrapper on E11 input and aligned the leader home test with the retrying toHaveURL idiom used elsewhere. --- src/lib/suggestionCatalog.ts | 8 ++++---- src/utilities/municipality/municipalityTriggers.ts | 6 +++--- tests/e2e/campaignMunicipalities.e2e.spec.ts | 8 +++----- tests/unit/suggestionCatalog.unit.spec.ts | 6 +++--- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/lib/suggestionCatalog.ts b/src/lib/suggestionCatalog.ts index ca931ade..c55dcdc6 100644 --- a/src/lib/suggestionCatalog.ts +++ b/src/lib/suggestionCatalog.ts @@ -95,8 +95,8 @@ export type MunicipalityTriggerInput = { coverageRatio: number | null coverageDeficit: number - /** Adversary signal within the window, if any (P1 variant) — presence reads as confirmed. */ - adversarySignal: { present: true } | null + /** Adversary signal within the window (P1 variant) — presence reads as confirmed. */ + hasAdversarySignal: boolean /** Any non-draft activity recently held or upcoming (the "agenda" leg of P1). */ hasRecentOrUpcomingActivity: boolean /** Activities `realizado` within the effort window (K-A's "esforço no ciclo"). */ @@ -314,7 +314,7 @@ const patternP1: SuggestionPattern = { evaluate: (input) => { if (input.territorialClass !== 'reduto' || !input.inCoreBlock) return null - const threatened = input.adversarySignal !== null + const threatened = input.hasAdversarySignal const own2018 = input.ownVotesByYear[YEAR_2018] ?? 0 const own2022 = input.ownVotesByYear[YEAR_2022] ?? 0 const dropped = own2018 > 0 && own2022 <= own2018 * SUGGESTION_ANCHORS.voteDropRatio @@ -339,7 +339,7 @@ const patternP1: SuggestionPattern = { } if (dormant) factors.push(`${signalAgeFactor(input.lastSignalAgeDays)}, sem agenda no período`) - const triageLevel: SuggestionTriageLevel = input.adversarySignal ? 1 : 3 + const triageLevel: SuggestionTriageLevel = input.hasAdversarySignal ? 1 : 3 return { patternId: 'P1', triageLevel, factors } }, } diff --git a/src/utilities/municipality/municipalityTriggers.ts b/src/utilities/municipality/municipalityTriggers.ts index 8ff58fce..0ed1c3cf 100644 --- a/src/utilities/municipality/municipalityTriggers.ts +++ b/src/utilities/municipality/municipalityTriggers.ts @@ -284,11 +284,11 @@ export const loadMunicipalitySuggestions = async ( ]) // Any adversary signal in the window reads as confirmed (B62: sinal = tipo + texto). - const adversarySignalByMunicipality = new Map() + const adversarySignalMunicipalityIDs = new Set() for (const signal of signals.docs) { const id = relationshipId(signal.municipality) if (id === null) continue - adversarySignalByMunicipality.set(id, { present: true }) + adversarySignalMunicipalityIDs.add(id) } const agendaByMunicipality = new Map() @@ -369,7 +369,7 @@ export const loadMunicipalitySuggestions = async ( lastPledgeAgeDays: municipalitySignalAgeInDays(pledgeAggregate.lastPledgeAt, now), coverageRatio: coverage.coverageRatio, coverageDeficit: coverage.deficit, - adversarySignal: adversarySignalByMunicipality.get(municipality.id) ?? null, + hasAdversarySignal: adversarySignalMunicipalityIDs.has(municipality.id), hasRecentOrUpcomingActivity: agenda?.hasAgenda ?? false, completedActivityCount: agenda?.completedCount ?? 0, } diff --git a/tests/e2e/campaignMunicipalities.e2e.spec.ts b/tests/e2e/campaignMunicipalities.e2e.spec.ts index 48feed19..e6d36d1d 100644 --- a/tests/e2e/campaignMunicipalities.e2e.spec.ts +++ b/tests/e2e/campaignMunicipalities.e2e.spec.ts @@ -301,11 +301,9 @@ test.describe('Municípios — jornadas por papel', () => { await expect(page.getByText('Cadastre apoiadores pelo celular.')).toBeVisible() // The leader redirect aborts `goto`'s load event (ERR_ABORTED); the - // redirect itself is the assertion, so navigate and wait for the target. - await Promise.all([ - page.waitForURL(`${campaign.baseURL}/campanha/contatos`), - page.goto(`${campaign.baseURL}/campanha/municipios`).catch(() => {}), - ]) + // redirect itself is the assertion, and `toHaveURL` retries onto it. + await page.goto(`${campaign.baseURL}/campanha/municipios`).catch(() => {}) + await expect(page).toHaveURL(`${campaign.baseURL}/campanha/contatos`) const supporterName = fixtures.value('Apoiador Liderança') const supporterPhone = fixtures.phone() diff --git a/tests/unit/suggestionCatalog.unit.spec.ts b/tests/unit/suggestionCatalog.unit.spec.ts index e62adbf1..2997bd8b 100644 --- a/tests/unit/suggestionCatalog.unit.spec.ts +++ b/tests/unit/suggestionCatalog.unit.spec.ts @@ -42,7 +42,7 @@ const input = (overrides: Partial = {}): MunicipalityT lastPledgeAgeDays: 3, coverageRatio: 0.6, coverageDeficit: 400, - adversarySignal: null, + hasAdversarySignal: false, hasRecentOrUpcomingActivity: false, completedActivityCount: 0, ...overrides, @@ -93,7 +93,7 @@ describe('P1 — reduto dormente ou ameaçado', () => { }) it('escalates to level 1 on any adversary signal — presence reads as confirmed', () => { - expect(evaluate('P1', reduto({ adversarySignal: { present: true } }))?.triageLevel).toBe(1) + expect(evaluate('P1', reduto({ hasAdversarySignal: true }))?.triageLevel).toBe(1) }) it('fires on a 2018→2022 vote drop even with a fresh signal', () => { @@ -315,7 +315,7 @@ describe('evaluateMunicipalityTriggers — ordering and silence', () => { territorialClass: 'reduto', inCoreBlock: true, lqByYear: { 2014: 2.5, 2018: 2.6, 2022: 2.4 }, - adversarySignal: { present: true }, + hasAdversarySignal: true, coverageRatio: 1.2, }), )