fix(ui): restore highlight modal toggle event & add dom test suite (v1.4.17)

This commit is contained in:
Kantine Wrapper
2026-02-24 20:40:54 +01:00
parent 7a82cb06db
commit c2e3282131
8 changed files with 132 additions and 21 deletions

View File

@@ -48,6 +48,11 @@ trigger: always_on
- **Browser**: Allowed for documentation and safe browsing. No automated logins without permission. - **Browser**: Allowed for documentation and safe browsing. No automated logins without permission.
- **Terminal**: No `rm -rf`. Run tests (`pytest` etc.) after logic changes. - **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 📋 ## 7. Requirements-Konsistenz 📋
Alle umgesetzten Anforderungen müssen mit `REQUIREMENTS.md` übereinstimmen. Alle umgesetzten Anforderungen müssen mit `REQUIREMENTS.md` übereinstimmen.
1. **Vor der Umsetzung prüfen**: Passt die neue Anforderung zu den bestehenden Requirements? 1. **Vor der Umsetzung prüfen**: Passt die neue Anforderung zu den bestehenden Requirements?

View File

@@ -258,6 +258,14 @@ if [ $LOGIC_EXIT -ne 0 ]; then
exit 1 exit 1
fi 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 ===" echo "=== Running Build Tests ==="
python3 "$SCRIPT_DIR/test_build.py" python3 "$SCRIPT_DIR/test_build.py"
TEST_EXIT=$? TEST_EXIT=$?

View File

@@ -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 ## 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. -**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.

15
debug_test.js Executable file
View File

@@ -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]);
}
}

View File

@@ -281,6 +281,18 @@
const historyModal = document.getElementById('history-modal'); const historyModal = document.getElementById('history-modal');
const btnHistoryClose = document.getElementById('btn-history-close'); 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', () => { btnHistory.addEventListener('click', () => {
if (!authToken) { if (!authToken) {
loginModal.classList.remove('hidden'); loginModal.classList.remove('hidden');

17
syntax_check.js Executable file
View File

@@ -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'));
}
}

View File

@@ -1,7 +1,12 @@
const fs = require('fs'); 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 = require('jsdom');
const { JSDOM } = jsdom; const { JSDOM } = jsdom;
log("Reading html...");
const html = ` const html = `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@@ -15,35 +20,80 @@ const html = `
<button id="alarm-bell" class="icon-btn hidden"> <button id="alarm-bell" class="icon-btn hidden">
<span id="alarm-bell-icon" style="color:var(--text-secondary);"></span> <span id="alarm-bell-icon" style="color:var(--text-secondary);"></span>
</button> </button>
<!-- Mocks for Highlights Feature -->
<button id="btn-highlights">Highlights</button>
<div id="highlights-modal" class="modal hidden">
<button id="btn-highlights-close">Close</button>
<input id="tag-input" type="text" />
<button id="btn-add-tag">Add</button>
<ul id="tags-list"></ul>
</div>
</body> </body>
</html> </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.window = dom.window;
global.document = window.document; global.document = window.document;
global.localStorage = { getItem: () => '[]', setItem: () => {} }; global.localStorage = { getItem: () => '[]', setItem: () => { } };
global.sessionStorage = { getItem: () => null }; global.sessionStorage = { getItem: () => null };
global.showToast = () => {}; global.showToast = () => { };
global.saveFlags = () => {}; global.saveFlags = () => { };
global.renderVisibleWeeks = () => {}; global.renderVisibleWeeks = () => { };
// Mock missing browser features if needed // 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 { log("Before eval...");
dom.window.eval(jsCode); const testCode = `
console.log("Initial Bell Classes:", window.document.getElementById('alarm-bell').className); console.log("--- Testing Alarm Bell ---");
// Add flag
// Add flag userFlags.add('2026-02-24_123'); updateAlarmBell();
dom.window.eval("userFlags.add('2026-02-24_123'); updateAlarmBell();"); if (document.getElementById('alarm-bell').className.includes('hidden')) throw new Error("Bell should be visible");
console.log("After Add:", window.document.getElementById('alarm-bell').className);
// Remove flag
// Remove flag userFlags.delete('2026-02-24_123'); updateAlarmBell();
dom.window.eval("userFlags.delete('2026-02-24_123'); updateAlarmBell();"); if (!document.getElementById('alarm-bell').className.includes('hidden')) throw new Error("Bell should be hidden");
console.log("After Remove:", window.document.getElementById('alarm-bell').className);
} catch (e) { console.log("✅ Alarm Bell Test Passed");
console.error(e);
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);

View File

@@ -1 +1 @@
v1.4.16 v1.4.17