import test from 'node:test'; import assert from 'node:assert/strict'; import {appearanceView, AppearanceUpdates} from '../src/appearance-state.ts'; const glass = () => ({requested_material:'frosted', effective_material:'frosted', opacity:90, native_effect:true, fallback_reason:null}); test('transparency requires a confirmed native effect', () => { assert.equal(appearanceView(undefined, false).material, 'solid'); assert.equal(appearanceView({...glass(), native_effect:false}, false).material, 'solid'); assert.equal(appearanceView(glass(), false).material, 'frosted'); assert.equal(appearanceView({...glass(), effective_material:'glass'}, false).material, 'glass'); }); test('high contrast stays opaque without losing the preference', () => { const status = glass(); assert.equal(appearanceView(status, true).material, 'solid'); assert.match(appearanceView(status, true).message, /preference is retained/); assert.equal(status.requested_material, 'frosted'); assert.equal(appearanceView(status, false).material, 'frosted'); }); test('fallback is honest and shell tint is bounded', () => { const fallback = {...glass(), effective_material:'solid', native_effect:false, fallback_reason:'Native effects unavailable.'}; assert.match(appearanceView(fallback, false).message, /Solid background/); assert.equal(appearanceView({...glass(), opacity:5}, false).opacity, 70); assert.equal(appearanceView({...glass(), opacity:110}, false).opacity, 100); assert.equal(appearanceView({...glass(), opacity:NaN}, false).opacity, 90); }); test('rapid pending changes serialize and only publish the latest response', async () => { const writes = [], published = [], errors = []; let resolve; let settled; const done = new Promise(r => settled = r); const queue = new AppearanceUpdates(update => { writes.push(update); if (writes.length === 1) return new Promise(r => resolve = r); return Promise.resolve({...glass(), opacity:update.preference.opacity}); }, status => published.push(status), error => errors.push(error), settled); queue.submit(false, {material:'glass', opacity:80}); queue.submit(false, {material:'frosted', opacity:95}); queue.submit(true); // Preserve the pending saved preference when contrast toggles. assert.equal(writes.length, 1); assert.equal(queue.busy, true); resolve({...glass(), effective_material:'glass', opacity:80}); await done; assert.equal(writes.length, 2); assert.equal(writes[1].highContrast, true); assert.deepEqual(writes[1].preference, {material:'frosted', opacity:95}); assert.equal(published.length, 1); assert.equal(published[0].opacity, 95); assert.equal(queue.busy, false); assert.deepEqual(errors, []); }); test('failed stale mutation cannot suppress a queued contrast restore', async () => { let reject, settled; const done = new Promise(r => settled = r); const writes = [], errors = [], published = []; const queue = new AppearanceUpdates(update => { writes.push(update); return writes.length === 1 ? new Promise((_,r) => reject=r) : Promise.resolve(glass()); }, status => published.push(status), error => errors.push(error), settled); queue.submit(true); queue.submit(false); reject(new Error('owned stale failure')); await done; assert.equal(writes.length,2); assert.equal(writes[1].highContrast,false); assert.equal(published.length,1); assert.deepEqual(errors,[]); });