teqo-backup/tests/e2e/campaignZoneMap.e2e.spec.ts
Francisco Ignácio Fontoura Solla 6a20e98047 fix(campanha): share one Drawer across municipalities mobile cards
Root cause of the prod-build React #130 on /campanha/municipios: the
md:hidden mobile-cards tree still hydrated ~25 rows x ~5 sheet controls
(~125 eager Drawer/DrawerCloseButton instances) even on wide viewports.
Filtered ?q=... routes failed more often only because they exercise the
list with filters active — the search param itself was not the bug.

CampaignListSheetProvider hosts a single bottom Drawer for the whole
mobile section; CampaignCellEditOverlay sheet cells portal their
body/footer into it while keeping per-cell open state and autosave
scaffolds. Footers with DrawerCloseButton are only created once a sheet
actually opens. Desktop popover path is unchanged; per-cell Drawer
remains the fallback for surfaces without the provider.

Also: DrawerCloseButton (function render prop, avoids base-ui element
render in prod bundles) replaces the DrawerClose render={<Button/>}
pattern across call sites; stale e2e selectors refreshed; e2e affected
script + playwright github/list reporters for CI; use-mobile reverted
(no consumers of the gate remain).

Guardrail filed as issue #52.
2026-07-30 17:37:20 -03:00

57 lines
2.4 KiB
TypeScript

import { expect, test } from './fixtures/campaignE2EFixtures.js'
/**
* B8 F2 — Salvador used to be one polygon carrying the whole city's votes, so
* the choropleth could not answer "which piece of Salvador is weak / is mine".
* This pins the two halves of that fix: the zone mesh is drawn (one path per
* ZE, the city's own polygon left underneath as a non-interactive base) and a
* zone is addressable on its own — the readout names it and opens it.
*/
test('the dashboard map paints Salvador zone by zone and opens one', async ({ campaign, page }) => {
const { fixtures } = campaign
const coordinator = await fixtures.createCampaignUser('coordinator', {
name: fixtures.value('Coordenadora Mapa'),
})
const password = coordinator.password
await campaign.login(page, coordinator.email!, password)
await page.goto('/campanha/quadro')
await expect(page.getByRole('heading', { name: 'Mapa dos Municípios' })).toBeVisible({
timeout: 60_000,
})
await page.waitForSelector('path.leaflet-interactive', { timeout: 60_000 })
// 417 municípios + 19 Salvador zones; Salvador's own polygon is the one path
// that stays out of the pointer's way.
await expect
.poll(
() =>
page.evaluate(() => {
const paths = Array.from(document.querySelectorAll('.leaflet-overlay-pane path'))
return {
total: paths.length,
interactive: paths.filter((path) => path.classList.contains('leaflet-interactive'))
.length,
}
}),
{ timeout: 30_000 },
)
.toEqual({ total: 436, interactive: 435 })
// Insertion order is z-order, so the zone mesh is the tail of the pane.
await page.evaluate(() => {
const paths = Array.from(document.querySelectorAll('.leaflet-overlay-pane path'))
paths.at(-1)?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }))
})
const readout = page.locator('div[aria-live="polite"]').first()
await expect(readout).toContainText(/Salvador — ZE \d+/)
await readout.getByRole('link', { name: 'Abrir município' }).click()
// The slug is the assertion: the detail page's own <h1> reads the municipality
// ROW, which other specs rename, while the polygon's name comes from the
// static catalog.
await expect(page).toHaveURL(/\/campanha\/municipios\/salvador-ze-\d+$/)
await expect(page.getByRole('heading', { level: 1 })).toBeVisible()
})