Počas nedávnej transformácie blogu, najmä migráciou zo Zoly na Astro, som začal pridávať funkcie ovládané env flagmi: komentáre, newsletter, sidebar s inými projektmi, upozornenie na preklad, digitálna záhrada. Funkcia komentárov prišla ako prvá, potom prechod na dvojjazyčnosť so samostatnou slovenskou doménou znamenal, že flag upozornenia na preklad musel fungovať inak pre každú lokalizáciu. Chcel som mať Playwright testy pokrývajúce toto všetko poriadne — teda funkcie zapnuté aj vypnuté, v angličtine aj slovenčine. Celkovo štyri kombinácie.
Chcel som mať testovacie pokrytie ako vždy, no dostať sa tam sa ukázalo byť oveľa dlhšou cestou, než som čakal.
Naivný prístup #
Môj prvý inštinkt bol ponechať dva buildy (EN a SK) a odovzdávať feature flagy za behu pri spúšťaní Wrangler dev servera:
wrangler pages dev dist-en --binding FEATURE_COMMENTS=true --port 4321
wrangler pages dev dist-en --binding FEATURE_COMMENTS= --port 4322
Flag --binding vyzeral sľubne. Nič nespravil. Feature flagy stále
prichádzali z .dev.vars, ktorý má tichú prednosť pred --binding bez
akéhokoľvek varovania. Na to som prišiel až vylúčením všetkého ostatného.
Ďalší pokus bol --env-file:
wrangler pages dev dist-en --env-file tests/env/features-on.env
Tento niečo robí, len nie to, čo chcete. Flag --env-file vkladá hodnoty
do Node.js process.env, procesu bežiaceho samotný Wrangler. Nedotýka sa
runtime prostredia Cloudflare Workera, z ktorého číta locals.runtime.env
vo vnútri Astro endpointu alebo server island. Worker teda nič nevidí,
flagy zostávajú vypnuté.
Pečenie funkcií do buildu #
V tomto bode bolo jasné, že jediná spoľahlivá možnosť je vpiect flagy
priamo do buildu. Vite má na to konfiguráciu define — robí doslovnú
substitúciu reťazcov počas buildu:
vite: {
define: {
"import.meta.env.FEATURE_COMMENTS": JSON.stringify(process.env.FEATURE_COMMENTS === "true"),
}
}
Zdalo sa, že to funguje, kým som neskontroloval skutočný súbor workera a nenašiel:
const showComments = false
Vpečené ako false aj vo features-on builde. Dôvod: SSR transform Astro
Cloudflare adaptéra prepísuje každý prístup import.meta.env.X na
process.env.X v bundle workera. Toto beží po Vite define passe a ticho to
zruší. Žiadna chyba sa neobjaví. Hodnota sa ticho stane tým, čo
process.env obsahuje za behu vo Workeri — čo nie je nič.
Riešením je použiť vlastné identifikátory, ktoré Astro nerozpozná ako
prístupy import.meta.env a teda ich neprepíše:
vite: {
define: {
__FEATURE_COMMENTS__: JSON.stringify(process.env.FEATURE_COMMENTS === "true"),
}
}
A v komponente:
const showComments = __FEATURE_COMMENTS__
Po tomto sa správne true alebo false vpečie do každého buildu.
Štyri buildy, zdieľané D1 #
So štyrmi samostatnými buildmi (EN off, EN on, SK off, SK on) testovací
setup spúšťa štyri Wrangler inštancie paralelne. Databáza D1 musela byť
dostupná zo všetkých. Keď každá bežala s vlastným adresárom stavu,
wrangler d1 migrations apply blog-db --local sa aplikovalo len na tú
inštanciu, ktorú náhodou našlo ako prvú. Ostatné tri buď preskočili alebo
ticho zlyhali s:
✅ No migrations to apply!
Aj keď tabuľka v skutočnosti ešte neexistovala.
Riešením je --persist-to .wrangler/state na každej inštancii:
wrangler pages dev dist-test/en-off --port 4321 --persist-to .wrangler/state
wrangler pages dev dist-test/en-on --port 4322 --persist-to .wrangler/state
wrangler pages dev dist-test/sk-off --port 4323 --persist-to .wrangler/state
wrangler pages dev dist-test/sk-on --port 4324 --persist-to .wrangler/state
Všetky štyri teraz zdieľajú rovnaký SQLite súbor pod .wrangler/state.
Migrácie sa aplikujú raz a všetky inštancie vidia výsledok.
Ešte pár prekvapení #
Server islands. Sekcia komentárov sa načítava cez Astro server:defer
— asynchrónne v prehliadači po načítaní stránky. Playwright skontroluje DOM
pred načítaním island, nenájde nič, test zlyhá. Pridanie
await page.waitForLoadState("networkidle") po navigácii to opraví.
Content Security Policy. Súbor public/_headers mal
script-src 'self' bez 'unsafe-inline'. Mechanizmus server island v
Astro vkladá obsah pomocou inline skriptov, ktoré prehliadač ticho
zablokuje. Island zostane zaseknutý na “Loading comments…” navždy.
Pridanie 'unsafe-inline' do script-src to opravilo. Za zmienku stojí,
že to platí aj v produkcii, keďže rovnaký súbor _headers sa nasadí na
Cloudflare Pages.
CSRF ochrana. Astro 5 štandardne zapína CSRF ochranu v SSR režime.
Kontroluje hlavičku Origin pri POST požiadavkách. API request context
Playwright ho neodosiela, takže každý API test dostal:
Cross-site POST form submissions are forbidden
ako 403 odpoveď. Opravené pridaním extraHTTPHeaders: { Origin: baseURL }
pre každý projekt v playwright.config.ts.
Výsledok #
Štyri Playwright projekty, každý smerujúci na vlastnú predzostavenú Wrangler inštanciu. Funkcie vpečené počas buildu, nie odovzdávané za behu. Jeden zdieľaný adresár stavu D1. Trvalo to dlhšie, než malo, ale setup je teraz čistý.