From c2e32821315b069403f052d75472b324d563f49a Mon Sep 17 00:00:00 2001 From: Kantine Wrapper Date: Tue, 24 Feb 2026 20:40:54 +0100 Subject: [PATCH] fix(ui): restore highlight modal toggle event & add dom test suite (v1.4.17) --- .agent/rules/rules.md | 5 +++ build-bookmarklet.sh | 8 ++++ changelog.md | 4 ++ debug_test.js | 15 ++++++++ kantine.js | 12 ++++++ syntax_check.js | 17 ++++++++ tests/test_dom.js | 90 +++++++++++++++++++++++++++++++++---------- version.txt | 2 +- 8 files changed, 132 insertions(+), 21 deletions(-) create mode 100755 debug_test.js create mode 100755 syntax_check.js diff --git a/.agent/rules/rules.md b/.agent/rules/rules.md index 9a88d21..9fcca07 100755 --- a/.agent/rules/rules.md +++ b/.agent/rules/rules.md @@ -48,6 +48,11 @@ trigger: always_on - **Browser**: Allowed for documentation and safe browsing. No automated logins without permission. - **Terminal**: No `rm -rf`. Run tests (`pytest` etc.) after logic changes. +## 7. Mandatory Testing Policy 🧪 +**CRITICAL: No logic or UI fix is complete without a corresponding automated test.** +- If you fix a regression or implement a new UI feature, you **MUST** write or update a test in `tests/test_dom.js` or `test_logic.js`. +- Refactoring MUST include verifying that no click listeners drop out. This guarantees that features like modal toggles stay functional. + ## 7. Requirements-Konsistenz 📋 Alle umgesetzten Anforderungen müssen mit `REQUIREMENTS.md` übereinstimmen. 1. **Vor der Umsetzung prüfen**: Passt die neue Anforderung zu den bestehenden Requirements? diff --git a/build-bookmarklet.sh b/build-bookmarklet.sh index 0cc89f3..7c004e0 100755 --- a/build-bookmarklet.sh +++ b/build-bookmarklet.sh @@ -258,6 +258,14 @@ if [ $LOGIC_EXIT -ne 0 ]; then exit 1 fi +echo "=== Running DOM Interaction Tests ===" +node "$SCRIPT_DIR/tests/test_dom.js" +DOM_EXIT=$? +if [ $DOM_EXIT -ne 0 ]; then + echo "❌ DOM UI tests FAILED! Regressions detected." + exit 1 +fi + echo "=== Running Build Tests ===" python3 "$SCRIPT_DIR/test_build.py" TEST_EXIT=$? diff --git a/changelog.md b/changelog.md index 064c122..937741d 100755 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +## v1.4.17 +- 🐛 **Bugfix**: Regression behoben: Der "Persönliche Highlights" (Stern-Button) Dialog öffnet sich nun wieder korrekt. +- 🧪 **Testing**: Es wurde ein initialer UI-Testing-Hook (`test_dom.js` mit `jsdom`) in die Build-Pipeline integriert, um kritische DOM Event-Listener Regressionen (wie den Highlights-Button und die Alarmglocke) automatisch zu preventen. + ## v1.4.16 - ⚡ **Feature**: Ein Button "Lokalen Cache leeren" wurde zum Versionen-Menü hinzugefügt, um bei hartnäckigen lokalen Fehlern alle Caches und Sessions bereinigen zu können, ohne die Entwicklertools (F12) des Browsers bemühen zu müssen. diff --git a/debug_test.js b/debug_test.js new file mode 100755 index 0000000..29940ac --- /dev/null +++ b/debug_test.js @@ -0,0 +1,15 @@ +const fs = require('fs'); +const jsCode = fs.readFileSync('kantine.js', 'utf8').replace('(function () {', '').replace(/}\)\(\);$/, ''); +try { + const vm = require('vm'); + new vm.Script(jsCode); +} catch (e) { + console.error(e.message); + const lines = jsCode.split('\n'); + console.error("Around line", e.loc?.line); + if(e.loc?.line) { + console.log(lines[e.loc.line - 2]); + console.log(lines[e.loc.line - 1]); + console.log(lines[e.loc.line]); + } +} diff --git a/kantine.js b/kantine.js index 7ab5ec9..981ab57 100755 --- a/kantine.js +++ b/kantine.js @@ -281,6 +281,18 @@ const historyModal = document.getElementById('history-modal'); const btnHistoryClose = document.getElementById('btn-history-close'); + if (btnHighlights) { + btnHighlights.addEventListener('click', () => { + highlightsModal.classList.remove('hidden'); + }); + } + + if (btnHighlightsClose) { + btnHighlightsClose.addEventListener('click', () => { + highlightsModal.classList.add('hidden'); + }); + } + btnHistory.addEventListener('click', () => { if (!authToken) { loginModal.classList.remove('hidden'); diff --git a/syntax_check.js b/syntax_check.js new file mode 100755 index 0000000..d0883d4 --- /dev/null +++ b/syntax_check.js @@ -0,0 +1,17 @@ +const fs = require('fs'); +const jsCode = fs.readFileSync('kantine.js', 'utf8') + .replace('(function () {', '') + .replace('})();', '') + .replace('if (window.__KANTINE_LOADED) return;', ''); +const testCode = ` + console.log("TEST"); +`; +const code = jsCode + '\n' + testCode; +try { + const vm = require('vm'); + new vm.Script(code); +} catch (e) { + if(e.stack) { + console.log("Syntax error at:", e.stack.split('\n').slice(0,3).join('\n')); + } +} diff --git a/tests/test_dom.js b/tests/test_dom.js index faacbf1..30502e9 100755 --- a/tests/test_dom.js +++ b/tests/test_dom.js @@ -1,7 +1,12 @@ const fs = require('fs'); +fs.writeFileSync('trace.log', ''); +function log(m) { fs.appendFileSync('trace.log', m + '\n'); } + +log("Initializing JSDOM..."); const jsdom = require('jsdom'); const { JSDOM } = jsdom; +log("Reading html..."); const html = ` @@ -15,35 +20,80 @@ const html = ` + + + + `; -const jsCode = fs.readFileSync('kantine.js', 'utf8').replace('(function () {', '').replace(/}\)\(\);$/, ''); +log("Reading file jsCode..."); +const jsCode = fs.readFileSync('kantine.js', 'utf8') + .replace('(function () {', '') + .replace('})();', '') + .replace('if (window.__KANTINE_LOADED) return;', ''); -const dom = new JSDOM(html, { runScripts: "dangerously" }); +log("Instantiating JSDOM..."); +const dom = new JSDOM(html, { runScripts: "dangerously", url: "http://localhost/" }); +log("JSDOM dom created..."); global.window = dom.window; global.document = window.document; -global.localStorage = { getItem: () => '[]', setItem: () => {} }; +global.localStorage = { getItem: () => '[]', setItem: () => { } }; global.sessionStorage = { getItem: () => null }; -global.showToast = () => {}; -global.saveFlags = () => {}; -global.renderVisibleWeeks = () => {}; +global.showToast = () => { }; +global.saveFlags = () => { }; +global.renderVisibleWeeks = () => { }; // Mock missing browser features if needed -global.Notification = { permission: 'default', requestPermission: () => {} }; +global.Notification = { permission: 'default', requestPermission: () => { } }; +global.window.matchMedia = () => ({ matches: false, addListener: () => { }, removeListener: () => { } }); +global.fetch = () => Promise.resolve({ ok: true, json: () => Promise.resolve({ results: [] }) }); +global.window.fetch = global.fetch; -try { - dom.window.eval(jsCode); - console.log("Initial Bell Classes:", window.document.getElementById('alarm-bell').className); - - // Add flag - dom.window.eval("userFlags.add('2026-02-24_123'); updateAlarmBell();"); - console.log("After Add:", window.document.getElementById('alarm-bell').className); - - // Remove flag - dom.window.eval("userFlags.delete('2026-02-24_123'); updateAlarmBell();"); - console.log("After Remove:", window.document.getElementById('alarm-bell').className); -} catch (e) { - console.error(e); +log("Before eval..."); +const testCode = ` + console.log("--- Testing Alarm Bell ---"); + // Add flag + userFlags.add('2026-02-24_123'); updateAlarmBell(); + if (document.getElementById('alarm-bell').className.includes('hidden')) throw new Error("Bell should be visible"); + + // Remove flag + userFlags.delete('2026-02-24_123'); updateAlarmBell(); + if (!document.getElementById('alarm-bell').className.includes('hidden')) throw new Error("Bell should be hidden"); + + console.log("✅ Alarm Bell Test Passed"); + + console.log("--- Testing Highlights Modal ---"); + // First, verify initial state + const hlModal = document.getElementById('highlights-modal'); + if (!hlModal.classList.contains('hidden')) throw new Error("Highlights modal should be hidden initially"); + + // Call bindEvents manually to attach the listeners since the IIFE is stripped + bindEvents(); + + // Click to open + document.getElementById('btn-highlights').click(); + if (hlModal.classList.contains('hidden')) throw new Error("Highlights modal did not open upon clicking btn-highlights!"); + + // Click to close + document.getElementById('btn-highlights-close').click(); + if (!hlModal.classList.contains('hidden')) throw new Error("Highlights modal did not close upon clicking btn-highlights-close!"); + + console.log("✅ Highlights Modal Test Passed"); + + window.__TEST_PASSED = true; + `; + +dom.window.eval(jsCode + "\n" + testCode); + +if (!dom.window.__TEST_PASSED) { + throw new Error("Tests failed to reach completion inside JSDOM."); } + +process.exit(0); diff --git a/version.txt b/version.txt index f776fe2..4c98421 100755 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v1.4.16 +v1.4.17