// changes.mjs sorts what a transaction touched into what the editor // has to do about it: text on a slide flows through y-prosemirror, // structure on a slide means the stage re-fetches it, and a change to // the slide list means the strip re-syncs. Driven with a real Y.Doc // and real observeDeep events, because the walk from an event's target // up to its slide is the whole of the module. import test from 'node:test' import assert from 'node:assert/strict' import * as Y from 'yjs' import { applyOps } from './ops.mjs' import { classify } from './changes.mjs' import { blockFragment } from './stage.mjs' function deck () { const doc = new Y.Doc({ guid: 'changes-test' }) doc.clientID = 3 applyOps(doc, [ { op: 'add_slide', id: 's1', layout: 'flow' }, { op: 'add_block', slide: 's1', id: 'b1', type: 'text', region: 'body', markdown: 'one' }, { op: 'add_slide', id: 's2', layout: 'flow' }, { op: 'add_block', slide: 's2', id: 'b2', type: 'heading', region: 'heading', markdown: 'two' } ], 'setup') const slides = doc.getArray('slides') let last = null slides.observeDeep(events => { last = classify(events, slides) }) return { doc, take: () => last } } test('typing into a block is a text change on its slide only', () => { const { doc, take } = deck() const f = blockFragment(doc, 's1', 'b1') doc.transact(() => { f.get(0).get(0).insert(0, 'x') }, 'typing') assert.deepEqual(take(), { text: ['s1'], structure: [], order: false }) }) test('a block\'s props changing is structure on its slide', () => { const { doc, take } = deck() applyOps(doc, [{ op: 'set_block', slide: 's2', id: 'b2', props: { level: 2 } }], 'ops') assert.deepEqual(take(), { text: [], structure: ['s2'], order: false }) }) test('adding, moving or removing a block is structure on its slide', () => { const { doc, take } = deck() applyOps(doc, [{ op: 'add_block', slide: 's1', id: 'b3', type: 'text', region: 'body', markdown: 'three' }], 'ops') assert.deepEqual(take(), { text: [], structure: ['s1'], order: false }) applyOps(doc, [{ op: 'remove_block', slide: 's1', id: 'b3' }], 'ops') assert.deepEqual(take(), { text: [], structure: ['s1'], order: false }) }) test('the notes are text on their slide', () => { const { doc, take } = deck() applyOps(doc, [{ op: 'set_notes', id: 's2', markdown: 'note' }], 'ops') assert.deepEqual(take(), { text: ['s2'], structure: [], order: false }) }) test('a slide added, removed or moved is an order change', () => { const { doc, take } = deck() applyOps(doc, [{ op: 'add_slide', id: 's3', layout: 'flow', after: 's1' }], 'ops') assert.deepEqual(take(), { text: [], structure: [], order: true }) applyOps(doc, [{ op: 'move_slide', id: 's3', after: 's2' }], 'ops') assert.deepEqual(take(), { text: [], structure: [], order: true }) applyOps(doc, [{ op: 'remove_slide', id: 's3' }], 'ops') assert.deepEqual(take(), { text: [], structure: [], order: true }) }) test('one transaction touching several things reports each once', () => { const { doc, take } = deck() applyOps(doc, [ { op: 'set_slide', id: 's1', template: 'section' }, { op: 'set_block', slide: 's1', id: 'b1', props: { size: 'l' } }, { op: 'set_text', slide: 's2', id: 'b2', markdown: 'changed' } ], 'ops') assert.deepEqual(take(), { text: ['s2'], structure: ['s1'], order: false }) })