mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-14 17:43:04 +00:00
The picker shipped as a capability of CampaignTable rather than a municípios feature, so every list that already uses the shared table got it: pass `columnVisibility` and the table mounts the menu and filters the columns. Persistence is a cookie, not localStorage, and the reason is a type rather than a preference: CampaignTableColumn.cell is a function, so the column array never crosses the server/client boundary and the client cannot rebuild the table without it. The server filters, so a hidden column does not reach the RSC payload either. `campaign_columns` carries a listId -> hidden ids map in a compact form with no JSON and no percent-encoding, because document.cookie and Next's cookies() disagree about decoding. Closing the menu is the commit. The first cut debounced at 400 ms, which batches a burst rather than a reading pace, so unchecking four columns while reading their labels was four full-route refreshes — on /campanha/liderancas each one re-pays the ~229 ms / 371-statement render already ledgered P1. The timer survives at 3 s as a safety net for a menu left open; the price is that the table behind the menu no longer updates per checkbox. `defaultVisible` is gone: it was true on 10 columns and false on none, and storing only the hidden set is what makes a newly shipped column visible to everyone who never touched the picker. In exchange `label` is required, which made `head` optional — 24 columns had a head that only restated the new label. Each surface declares one mandatory column, re-filtered on the server so a stale cookie cannot leave a row without its only linked cell. The bug review caught is this codebase's recurring one: the RSC payload of toggle A arrives inside the window of toggle B, and adopting the server there undid B and then committed the set without it. Reconciliation now runs only when no write is pending, pinned red against the previous code. Municipality column ids are typed against MunicipalityListColumnId now, so the id, the B22 description and the picker label are one key; the union member `nivel` was renamed `level` to match the id and the ?level= filter. The picker's own content comparison is sameIdSet, widened to string ids.
100 lines
3.9 KiB
TypeScript
100 lines
3.9 KiB
TypeScript
import { expect, test } from './fixtures/campaignE2EFixtures.js'
|
|
|
|
/**
|
|
* B17 — the column picker is shared by every `CampaignTable` surface, so what
|
|
* is pinned here is the contract, not one screen: hiding survives a reload
|
|
* (cookie), one list's choice does not leak into another, and the mandatory
|
|
* column cannot be unchecked.
|
|
*
|
|
* Own file on purpose: `campaignMunicipalities.e2e.spec.ts` carries two
|
|
* deterministic failures at one worker, ledgered as P1.
|
|
*/
|
|
/**
|
|
* Toggling costs a full RSC round-trip of a heavy list route. In dev, with the
|
|
* suite's two workers competing for the same server, that overruns the 5 s
|
|
* default of `expect` — so the assertions that wait on the refresh get their
|
|
* own budget instead of a flake nobody can reproduce at one worker.
|
|
*/
|
|
const REFRESH = { timeout: 20_000 }
|
|
|
|
test.describe('Seletor de colunas', () => {
|
|
test('hides a column, keeps it hidden across reloads and restores it', async ({
|
|
campaign,
|
|
page,
|
|
}) => {
|
|
// Crosses two heavy list routes; `/campanha/liderancas` only finishes
|
|
// compiling in dev when an authenticated request reaches its tree.
|
|
test.slow()
|
|
|
|
const { fixtures } = campaign
|
|
const password = fixtures.value('senha')
|
|
const email = `${fixtures.value('colunas-coordinator')}@example.com`
|
|
await campaign.payload.create({
|
|
collection: 'campaignUser',
|
|
data: {
|
|
name: fixtures.value('Coordenadora Colunas'),
|
|
email,
|
|
password,
|
|
role: 'coordinator',
|
|
},
|
|
depth: 0,
|
|
})
|
|
|
|
await campaign.login(page, email, password)
|
|
await page.goto('/campanha/municipios')
|
|
|
|
const kindHeader = page.getByRole('columnheader', { name: /Tipo/ })
|
|
await expect(kindHeader).toBeVisible()
|
|
|
|
const picker = page.getByRole('button', { name: /Mostrar ou ocultar colunas/ })
|
|
await picker.click()
|
|
await page.getByRole('checkbox', { name: 'Tipo' }).click()
|
|
// Closing flushes the batched cookie write and refreshes the RSC.
|
|
await page.keyboard.press('Escape')
|
|
|
|
await expect(kindHeader).toHaveCount(0, REFRESH)
|
|
await expect(page.getByRole('button', { name: /1 oculta/ })).toBeVisible()
|
|
|
|
await page.reload()
|
|
await expect(page.getByRole('columnheader', { name: /Tipo/ })).toHaveCount(0, REFRESH)
|
|
|
|
// The cookie carries one key per list: another list is untouched. The
|
|
// territories list is the honest neighbour here — its 27 rows come from
|
|
// the catalog, so it never falls into an empty state.
|
|
await page.goto('/campanha/territorios')
|
|
await expect(page.getByRole('columnheader', { name: /Municípios/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'Mostrar ou ocultar colunas' })).toBeVisible()
|
|
|
|
await page.goto('/campanha/municipios')
|
|
await page.getByRole('button', { name: /Mostrar ou ocultar colunas/ }).click()
|
|
await page.getByRole('button', { name: 'Restaurar todas' }).click()
|
|
await page.keyboard.press('Escape')
|
|
|
|
await expect(page.getByRole('columnheader', { name: /Tipo/ })).toBeVisible(REFRESH)
|
|
})
|
|
|
|
test('lists the mandatory column without letting it be unchecked', async ({ campaign, page }) => {
|
|
const { fixtures } = campaign
|
|
const password = fixtures.value('senha')
|
|
const email = `${fixtures.value('colunas-mandatory')}@example.com`
|
|
await campaign.payload.create({
|
|
collection: 'campaignUser',
|
|
data: {
|
|
name: fixtures.value('Coordenadora Obrigatória'),
|
|
email,
|
|
password,
|
|
role: 'coordinator',
|
|
},
|
|
depth: 0,
|
|
})
|
|
|
|
await campaign.login(page, email, password)
|
|
await page.goto('/campanha/municipios')
|
|
|
|
await page.getByRole('button', { name: /Mostrar ou ocultar colunas/ }).click()
|
|
// The name carries the reason too ("Município sempre visível").
|
|
const mandatory = page.getByRole('checkbox', { name: /^Município/ })
|
|
await expect(mandatory).toBeChecked()
|
|
await expect(mandatory).toBeDisabled()
|
|
})
|
|
})
|