[grade=A urn:ump:zmnws7b7c63n4sr4qr6collvmttg4ksuaju4z2ndhxo5lntczfbq] test(DC-168): update widget-contract sandbox for batch-2 widget contract

The contract test eval()s the real calendar-widget.js in a hand-rolled
sandbox; batch 2 (3a0f595) changed the contract the sandbox must provide:
- makeEl stubs gain classList (widget uses the class-based .show modal
  system now; style.display checks threw TypeError on old stubs)
- r14 modal trigger: classList.add('show') instead of display='flex'
- ErrorHandler stubs gain logError (catch path calls logError now)
- secureFetch stub provided before every widget eval (all widget API
  calls go through secureFetch now)
Post-grade polish under same URN: removed 4 leftover DBG console.log
lines (judge polish item; test-only, no assertion change).

File 9/9 green; full suite 172/172 suites, 3731/3731 tests.
This commit is contained in:
Hermes committed 2026-10-28 00:34:14 -07:00
1 parent 4339d430f6
commit db99f2b54d
1 file changed
+19 -17
@@ -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;