oficina / slides Public

Clone
git clone https://amadan.net/oficina/slides

Plain git — no account needed to clone.

Download

Download this file

1// status.mjs: the save line's state from what the save queue and the
2// resync controller report, in estilo savestatus's words, and the load
3// error in its own line. The case this exists for: a batch the server
4// refused with a 4xx is dropped, not retried, and the line has to say
5// so rather than "Offline, retrying" — and keep saying so.
6import test from 'node:test'
7import assert from 'node:assert/strict'
8import { queueStatusState, statusState, createEditorStatus, mountSaveStatus } from './status.mjs'
9
10test('the queue words map to savestatus states, failed staying failed', () => {
11 assert.equal(queueStatusState('ok'), 'saved')
12 assert.equal(queueStatusState('retrying'), 'retrying')
13 assert.equal(queueStatusState('failed'), 'failed')
14})
15
16test('a dropped batch outranks the queue, for the rest of the session', () => {
17 assert.equal(statusState({ queueState: 'saved' }), 'saved')
18 assert.equal(statusState({ queueState: 'saving' }), 'saving')
19 assert.equal(statusState({ queueState: 'retrying' }), 'retrying')
20 assert.equal(statusState({ queueState: 'retrying', sessionFailed: true }), 'failed')
21 // A later successful save does not erase a drop from view.
22 assert.equal(statusState({ queueState: 'saved', sessionFailed: true }), 'failed')
23 assert.equal(statusState(), 'saved')
24})
25
26test('the editor status: the save line and the load error are two lines', () => {
27 const states = []
28 const error = { textContent: '' }
29 const status = createEditorStatus({ setSave: s => states.push(s), errorEl: error, errorText: 'load-error' })
30 status.save('saving')
31 assert.equal(states.at(-1), 'saving')
32 status.loadFailed(true)
33 assert.equal(error.textContent, 'load-error')
34 assert.equal(states.at(-1), 'saving', 'a load error does not touch the save line')
35 status.save('saved')
36 assert.equal(states.at(-1), 'saved')
37 assert.equal(error.textContent, 'load-error', 'a save does not clear the load error')
38 status.loadFailed(false)
39 assert.equal(error.textContent, '')
40 status.drop()
41 assert.equal(states.at(-1), 'failed')
42 status.save('saved')
43 assert.equal(states.at(-1), 'failed', 'dropped stays dropped')
44})
45
46test('mountSaveStatus loads estilo\'s module and mounts it on the line', async () => {
47 const line = { id: 'line' }
48 const error = { textContent: '' }
49 const root = {
50 dataset: { saveStatusModule: '/static/estilo/save-status.x.mjs', textError: 'load-error' },
51 querySelector: sel => (sel === '[estilo-save-status]' ? line : sel === '[slides-load-error]' ? error : null)
52 }
53 let mounted = null
54 let set = []
55 const status = await mountSaveStatus(root, async src => {
56 assert.equal(src, '/static/estilo/save-status.x.mjs')
57 return { mount: el => { mounted = el; return s => set.push(s) } }
58 })
59 assert.equal(mounted, line)
60 status.save('saving')
61 assert.deepEqual(set, ['saving'])
62})
63
64test('a save-status module that will not load says so and refuses to mount', async () => {
65 const error = { textContent: '' }
66 const root = {
67 dataset: { saveStatusModule: '/nope.mjs', textError: 'load-error' },
68 querySelector: sel => (sel === '[slides-load-error]' ? error : null)
69 }
70 await assert.rejects(mountSaveStatus(root, async () => { throw new Error('unavailable') }))
71 assert.equal(error.textContent, 'load-error')
72})
73