fix(ui): restore highlight modal toggle event & add dom test suite (v1.4.17)
This commit is contained in:
@@ -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?
|
||||||
|
|||||||
@@ -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=$?
|
||||||
|
|||||||
@@ -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
15
debug_test.js
Executable 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
kantine.js
12
kantine.js
@@ -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
17
syntax_check.js
Executable 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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v1.4.16
|
v1.4.17
|
||||||
|
|||||||
Reference in New Issue
Block a user