// @ts-check const { test, expect } = require('@playwright/test'); /** * Stufe 1 · Regression: Fluss-Live-State liefert echte Werte * * Bug-Historie (2026-06-11): GGS_LIVE_STATE in Fluss prüfte `game.params`, * aber `params` lebt nicht auf `game` selbst — es ist nur ein berechnetes * Result aus `E.getStatus(game)`. Daher gab GGS_LIVE_STATE() immer null * zurück und der Lehrer-Cockpit zeigte "Diese Simulation meldet noch * keine Live-Werte" trotz aktiver Sitzung. * * Fix: GGS_LIVE_STATE ruft jetzt `E.getStatus(game)` und liest `st.params`. * * Dieser Test ruft die Sim mit ?level=1 auf (überspringt den Level-Picker), * wartet bis die Engine boots, und prüft dass GGS_LIVE_STATE() ein Objekt * mit den erwarteten Schlüsseln liefert. */ // baseURL aus Config ist ohne trailing slash → page.goto('/fluss') würde lokal // fälschlich auf http://localhost/fluss (404) gehen. Wir konstruieren die URL // daher absolut. Funktioniert für Localhost mit Subpath UND Prod ohne Subpath. const BASE = (process.env.GGS_BASE_URL || 'http://localhost/geograsim/App').replace(/\/$/, ''); test('Fluss · GGS_LIVE_STATE liefert nach Boot echte Werte', async ({ page }) => { const errors = []; page.on('pageerror', err => errors.push('PAGEERROR: ' + err.message)); page.on('console', msg => { if (msg.type() === 'error') errors.push('CONSOLE: ' + msg.text().substring(0, 200)); }); const res = await page.goto(BASE + '/fluss?level=1'); expect(res?.status(), 'Wrapper /fluss antwortet nicht').toBeLessThan(400); // Auf Engine-Boot warten — Fluss setzt window.FLUSS in init() nach createGame. await page.waitForTimeout(2500); const info = await page.evaluate(() => ({ ggs: typeof window['__GGS__'], engine: typeof window['FlussEngine'], fluss: typeof window['FLUSS'], state: typeof window['GGS_LIVE_STATE'], })); expect(info.ggs, `__GGS__ fehlt (errors: ${errors.join(' | ')})`).toBe('object'); expect(info.engine, `FlussEngine fehlt (errors: ${errors.join(' | ')})`).toBe('object'); expect(info.state, `GGS_LIVE_STATE fehlt (errors: ${errors.join(' | ')})`).toBe('function'); const snapshot = await page.evaluate(() => { // @ts-ignore const fn = window['GGS_LIVE_STATE']; if (typeof fn !== 'function') return null; try { return fn(); } catch (e) { return { _error: String(e) }; } }); // 1) Snapshot muss ein Objekt sein, nicht null expect(snapshot, 'GGS_LIVE_STATE() lieferte null (params-Bug?)').not.toBeNull(); expect(typeof snapshot, 'GGS_LIVE_STATE() lieferte kein Objekt').toBe('object'); expect(snapshot._error, 'GGS_LIVE_STATE() warf Exception: ' + snapshot?._error).toBeUndefined(); // 2) Pflichtfelder fürs Cockpit expect(snapshot.simId).toBe('fluss'); expect(typeof snapshot.year).toBe('number'); expect(typeof snapshot.progressPct).toBe('number'); expect(typeof snapshot.budget).toBe('number'); // 3) Engine-Werte (params) — müssen Zahlen sein, nicht undefined expect(typeof snapshot.biodiv, 'biodiv fehlt — params-Bug?').toBe('number'); expect(typeof snapshot.economy, 'economy fehlt — params-Bug?').toBe('number'); expect(typeof snapshot.flood, 'flood fehlt — params-Bug?').toBe('number'); expect(typeof snapshot.population, 'population fehlt — params-Bug?').toBe('number'); expect(typeof snapshot.food, 'food fehlt — params-Bug?').toBe('number'); // 4) Plausibilitäts-Range — params kommen aus clamp100, also 0-100 (außer population) expect(snapshot.biodiv).toBeGreaterThanOrEqual(0); expect(snapshot.biodiv).toBeLessThanOrEqual(100); expect(snapshot.flood).toBeGreaterThanOrEqual(0); expect(snapshot.flood).toBeLessThanOrEqual(100); // 5) progressPct == 0 am Spielanfang (Jahr 0), darf aber bis 100 wachsen expect(snapshot.progressPct).toBeGreaterThanOrEqual(0); expect(snapshot.progressPct).toBeLessThanOrEqual(100); });