diff --git a/dashcaddy-api/__tests__/calendar/dc168-widget-contract.test.js b/dashcaddy-api/__tests__/calendar/dc168-widget-contract.test.js index efbf37e..5e0da29 100644 --- a/dashcaddy-api/__tests__/calendar/dc168-widget-contract.test.js +++ b/dashcaddy-api/__tests__/calendar/dc168-widget-contract.test.js @@ -102,7 +102,7 @@ describe('DC-168 r2: fetch window follows the visible month', () => { const elements = {}; function makeEl(id) { return elements[id] || (elements[id] = { - textContent: '', innerHTML: '', value: '', style: { display: '' }, + classList: { _s: new Set(), add(c) { this._s.add(c); }, remove(c) { this._s.delete(c); }, contains(c) { return this._s.has(c); } }, textContent: '', innerHTML: '', value: '', style: { display: '' }, listeners: {}, addEventListener(ev, fn) { this.listeners[ev] = fn; }, querySelector() { return null; }, dataset: {}, @@ -125,7 +125,7 @@ describe('DC-168 r2: fetch window follows the visible month', () => { }; global.window = { CALENDAR: 300000 }; global.__logged = []; - global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); }; + global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); this.logError = (ctx, e) => global.__logged.push(String(e || ctx)); }; global.injectModal = () => {}; global.confirm = () => true; global.alert = () => {}; @@ -141,6 +141,7 @@ describe('DC-168 r2: fetch window follows the visible month', () => { }; const src = fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-widget.js'), 'utf8'); + global.secureFetch = global.fetch; // widget API calls go through secureFetch (batch 2) eval(src); global.setTimeout = realSetTimeout; @@ -168,7 +169,7 @@ describe('DC-168 r4 integration: widget + cache against changing server data', ( const elements = {}; function makeEl(id) { return elements[id] || (elements[id] = { - textContent: '', innerHTML: '', value: '', style: { display: '' }, + classList: { _s: new Set(), add(c) { this._s.add(c); }, remove(c) { this._s.delete(c); }, contains(c) { return this._s.has(c); } }, textContent: '', innerHTML: '', value: '', style: { display: '' }, listeners: {}, addEventListener(ev, fn) { this.listeners[ev] = fn; }, querySelector() { return null; }, dataset: {}, }); @@ -190,7 +191,7 @@ describe('DC-168 r4 integration: widget + cache against changing server data', ( }; global.window = { CALENDAR: 300000 }; global.__logged = []; - global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); }; + global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); this.logError = (ctx, e) => global.__logged.push(String(e || ctx)); }; global.injectModal = () => {}; global.confirm = () => true; global.alert = () => {}; @@ -210,6 +211,7 @@ describe('DC-168 r4 integration: widget + cache against changing server data', ( const csrc = fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-cache.js'), 'utf8'); eval(csrc); const wsrc = fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-widget.js'), 'utf8'); + global.secureFetch = global.fetch; // widget API calls go through secureFetch (batch 2) eval(wsrc); global.setTimeout = realSetTimeout; expect(global.window.CalendarCache).toBeDefined(); @@ -219,7 +221,6 @@ describe('DC-168 r4 integration: widget + cache against changing server data', ( await new Promise(r => setTimeout(r, 25)); let textEl = elements['.calendar-widget-text']; if (!textEl.innerHTML.includes('Stays')) { - console.log('DBG:', JSON.stringify(textEl.innerHTML), 'ERR:', global.__logged.join('||').slice(0, 300)); } // after fetch #1, chip shows the earliest of the two (Stays @3d) expect(textEl.innerHTML).toContain('Stays'); @@ -244,7 +245,7 @@ describe('DC-168 r7: genuinely disjoint intervals + gap navigation', () => { const elements = {}; function makeEl(id) { return elements[id] || (elements[id] = { - textContent: '', innerHTML: '', value: '', style: { display: '' }, + classList: { _s: new Set(), add(c) { this._s.add(c); }, remove(c) { this._s.delete(c); }, contains(c) { return this._s.has(c); } }, textContent: '', innerHTML: '', value: '', style: { display: '' }, listeners: {}, addEventListener(ev, fn) { this.listeners[ev] = fn; }, querySelector() { return null; }, dataset: {}, }); @@ -266,7 +267,7 @@ describe('DC-168 r7: genuinely disjoint intervals + gap navigation', () => { }; global.window = { CALENDAR: 300000 }; global.__logged = []; - global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); }; + global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); this.logError = (ctx, e) => global.__logged.push(String(e || ctx)); }; global.injectModal = () => {}; global.confirm = () => true; global.alert = () => {}; @@ -278,6 +279,7 @@ describe('DC-168 r7: genuinely disjoint intervals + gap navigation', () => { const csrc = fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-cache.js'), 'utf8'); eval(csrc); const wsrc = fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-widget.js'), 'utf8'); + global.secureFetch = global.fetch; // widget API calls go through secureFetch (batch 2) eval(wsrc); global.setTimeout = realSetTimeout; return { elements, fetchCalls }; @@ -317,7 +319,7 @@ describe('DC-168 r14: chip correctness after month navigation', () => { const elements = {}; function makeEl(id) { return elements[id] || (elements[id] = { - textContent: '', innerHTML: '', value: '', style: { display: '' }, + classList: { _s: new Set(), add(c) { this._s.add(c); }, remove(c) { this._s.delete(c); }, contains(c) { return this._s.has(c); } }, textContent: '', innerHTML: '', value: '', style: { display: '' }, listeners: {}, addEventListener(ev, fn) { this.listeners[ev] = fn; }, querySelector() { return null; }, dataset: {}, }); @@ -339,7 +341,7 @@ describe('DC-168 r14: chip correctness after month navigation', () => { }; global.window = { CALENDAR: 300000 }; global.__logged = []; - global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); }; + global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); this.logError = (ctx, e) => global.__logged.push(String(e || ctx)); }; global.injectModal = () => {}; global.confirm = () => true; global.alert = () => {}; @@ -360,13 +362,13 @@ describe('DC-168 r14: chip correctness after month navigation', () => { const csrc = fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-cache.js'), 'utf8'); eval(csrc); const wsrc = fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-widget.js'), 'utf8'); + global.secureFetch = global.fetch; // widget API calls go through secureFetch (batch 2) eval(wsrc); global.setTimeout = realSetTimeout; await global.window.refreshCalendarWidget(); await new Promise(r => setTimeout(r, 25)); let textEl = elements['.calendar-widget-text']; - console.log('DBG chip:', JSON.stringify(textEl.innerHTML).slice(0,120), 'ERR:', global.__logged.join('||').slice(0,250)); expect(textEl.innerHTML).toContain('Soon Real'); // navigate forward to the far month (stepped; each nav refetches when the @@ -376,13 +378,11 @@ describe('DC-168 r14: chip correctness after month navigation', () => { await new Promise(r => setTimeout(r, 20)); } textEl = elements['.calendar-widget-text']; - console.log('DBG post-nav chip:', JSON.stringify(textEl.innerHTML).slice(0,160), 'ERR:', global.__logged.join('||').slice(0,300)); expect(textEl.innerHTML).toContain('Soon Real'); expect(textEl.innerHTML).not.toContain('Far Viewed'); // forced refresh with the view still on the far month: chip unchanged await global.window.refreshCalendarWidget(true); textEl = elements['.calendar-widget-text']; - console.log('DBG post-forced chip:', JSON.stringify(textEl.innerHTML).slice(0,160)); expect(textEl.innerHTML).toContain('Soon Real'); expect(textEl.innerHTML).not.toContain('Far Viewed'); }); @@ -398,7 +398,7 @@ describe('DC-168 r14: chip correctness after month navigation', () => { const elements = {}; function makeEl(id) { return elements[id] || (elements[id] = { - textContent: '', innerHTML: '', value: '', style: { display: '' }, + classList: { _s: new Set(), add(c) { this._s.add(c); }, remove(c) { this._s.delete(c); }, contains(c) { return this._s.has(c); } }, textContent: '', innerHTML: '', value: '', style: { display: '' }, listeners: {}, addEventListener() {}, querySelector() { return null; }, dataset: {}, }); } @@ -412,16 +412,17 @@ describe('DC-168 r14: chip correctness after month navigation', () => { body: { appendChild() {} }, }; global.window = { CALENDAR: 300000 }; - global.ErrorHandler = function () { this.log = () => {}; }; + global.ErrorHandler = function () { this.log = () => {}; this.logError = () => {}; }; global.injectModal = () => {}; global.fetch = async () => ({ status: 200, ok: true, json: async () => body }); const realSetTimeout = global.setTimeout; global.setInterval = () => {}; global.setTimeout = (f) => { f(); }; + global.secureFetch = global.fetch; // widget API calls go through secureFetch (batch 2) eval(fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-widget.js'), 'utf8')); global.setTimeout = realSetTimeout; elements['calendar-modal'] = elements['calendar-modal'] || makeEl('calendar-modal'); - elements['calendar-modal'].style.display = 'flex'; // renderUpcoming runs when the modal is open + elements['calendar-modal'].classList.add('show'); // renderUpcoming runs when the modal is open (widget uses .show like every modal) await global.window.refreshCalendarWidget(); await new Promise(r => setTimeout(r, 25)); const list = elements['calendar-upcoming-list']; @@ -440,7 +441,7 @@ describe('DC-168 r15: non-forced refresh with navigated viewMonth', () => { const elements = {}; function makeEl(id) { return elements[id] || (elements[id] = { - textContent: '', innerHTML: '', value: '', style: { display: '' }, + classList: { _s: new Set(), add(c) { this._s.add(c); }, remove(c) { this._s.delete(c); }, contains(c) { return this._s.has(c); } }, textContent: '', innerHTML: '', value: '', style: { display: '' }, listeners: {}, addEventListener(ev, fn) { this.listeners[ev] = fn; }, querySelector() { return null; }, dataset: {}, }); @@ -462,7 +463,7 @@ describe('DC-168 r15: non-forced refresh with navigated viewMonth', () => { }; global.window = { CALENDAR: 300000 }; global.__logged = []; - global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); }; + global.ErrorHandler = function () { this.log = (e) => global.__logged.push(String(e)); this.logError = (ctx, e) => global.__logged.push(String(e || ctx)); }; global.injectModal = () => {}; global.confirm = () => true; global.alert = () => {}; @@ -476,6 +477,7 @@ describe('DC-168 r15: non-forced refresh with navigated viewMonth', () => { { title: 'Far Viewed', start: farMonth }, ] }) }); eval(fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-cache.js'), 'utf8')); + global.secureFetch = global.fetch; // widget API calls go through secureFetch (batch 2) eval(fs.readFileSync(path.join(REPO_ROOT, 'status/js/calendar-widget.js'), 'utf8')); global.setTimeout = realSetTimeout;