// status.mjs: the save line's state from what the save queue and the // resync controller report, in estilo savestatus's words, and the load // error in its own line. The case this exists for: a batch the server // refused with a 4xx is dropped, not retried, and the line has to say // so rather than "Offline, retrying" — and keep saying so. import test from 'node:test' import assert from 'node:assert/strict' import { queueStatusState, statusState, createEditorStatus, mountSaveStatus } from './status.mjs' test('the queue words map to savestatus states, failed staying failed', () => { assert.equal(queueStatusState('ok'), 'saved') assert.equal(queueStatusState('retrying'), 'retrying') assert.equal(queueStatusState('failed'), 'failed') }) test('a dropped batch outranks the queue, for the rest of the session', () => { assert.equal(statusState({ queueState: 'saved' }), 'saved') assert.equal(statusState({ queueState: 'saving' }), 'saving') assert.equal(statusState({ queueState: 'retrying' }), 'retrying') assert.equal(statusState({ queueState: 'retrying', sessionFailed: true }), 'failed') // A later successful save does not erase a drop from view. assert.equal(statusState({ queueState: 'saved', sessionFailed: true }), 'failed') assert.equal(statusState(), 'saved') }) test('the editor status: the save line and the load error are two lines', () => { const states = [] const error = { textContent: '' } const status = createEditorStatus({ setSave: s => states.push(s), errorEl: error, errorText: 'load-error' }) status.save('saving') assert.equal(states.at(-1), 'saving') status.loadFailed(true) assert.equal(error.textContent, 'load-error') assert.equal(states.at(-1), 'saving', 'a load error does not touch the save line') status.save('saved') assert.equal(states.at(-1), 'saved') assert.equal(error.textContent, 'load-error', 'a save does not clear the load error') status.loadFailed(false) assert.equal(error.textContent, '') status.drop() assert.equal(states.at(-1), 'failed') status.save('saved') assert.equal(states.at(-1), 'failed', 'dropped stays dropped') }) test('mountSaveStatus loads estilo\'s module and mounts it on the line', async () => { const line = { id: 'line' } const error = { textContent: '' } const root = { dataset: { saveStatusModule: '/static/estilo/save-status.x.mjs', textError: 'load-error' }, querySelector: sel => (sel === '[estilo-save-status]' ? line : sel === '[slides-load-error]' ? error : null) } let mounted = null let set = [] const status = await mountSaveStatus(root, async src => { assert.equal(src, '/static/estilo/save-status.x.mjs') return { mount: el => { mounted = el; return s => set.push(s) } } }) assert.equal(mounted, line) status.save('saving') assert.deepEqual(set, ['saving']) }) test('a save-status module that will not load says so and refuses to mount', async () => { const error = { textContent: '' } const root = { dataset: { saveStatusModule: '/nope.mjs', textError: 'load-error' }, querySelector: sel => (sel === '[slides-load-error]' ? error : null) } await assert.rejects(mountSaveStatus(root, async () => { throw new Error('unavailable') })) assert.equal(error.textContent, 'load-error') })