mirror of https://github.com/artf/grapesjs.git
2 changed files with 184 additions and 0 deletions
@ -0,0 +1,92 @@ |
|||||
|
import PatchManager, { PatchManagerEvents } from 'patch_manager'; |
||||
|
|
||||
|
describe('PatchManager', () => { |
||||
|
test('Records a patch during update and emits update event', () => { |
||||
|
const events = []; |
||||
|
const pm = new PatchManager({ |
||||
|
enabled: true, |
||||
|
emitter: { |
||||
|
trigger: (event, payload) => events.push({ event, payload }), |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
pm.update(() => { |
||||
|
const patch = pm.createOrGetCurrentPatch(); |
||||
|
patch.changes.push({ op: 'replace', path: ['value'], value: 1 }); |
||||
|
patch.reverseChanges.push({ op: 'replace', path: ['value'], value: 0 }); |
||||
|
}); |
||||
|
|
||||
|
expect(events).toHaveLength(1); |
||||
|
expect(events[0].event).toBe(PatchManagerEvents.update); |
||||
|
expect(events[0].payload.changes).toHaveLength(1); |
||||
|
expect(events[0].payload.reverseChanges).toHaveLength(1); |
||||
|
}); |
||||
|
|
||||
|
test('Applies patches and respects the external flag', () => { |
||||
|
const calls = []; |
||||
|
const events = []; |
||||
|
const pm = new PatchManager({ |
||||
|
enabled: true, |
||||
|
applyPatch: (changes, options) => calls.push({ changes, options }), |
||||
|
emitter: { |
||||
|
trigger: (event) => events.push(event), |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const patch = { |
||||
|
id: 'patch-1', |
||||
|
changes: [{ op: 'add', path: ['value'], value: 1 }], |
||||
|
reverseChanges: [{ op: 'remove', path: ['value'] }], |
||||
|
}; |
||||
|
|
||||
|
pm.apply(patch); |
||||
|
|
||||
|
expect(calls).toHaveLength(1); |
||||
|
expect(calls[0]).toEqual({ |
||||
|
changes: patch.changes, |
||||
|
options: { external: false, direction: 'forward' }, |
||||
|
}); |
||||
|
expect(events).toEqual([PatchManagerEvents.update]); |
||||
|
|
||||
|
calls.length = 0; |
||||
|
events.length = 0; |
||||
|
|
||||
|
pm.apply(patch, { external: true }); |
||||
|
|
||||
|
expect(calls).toHaveLength(1); |
||||
|
expect(calls[0]).toEqual({ |
||||
|
changes: patch.changes, |
||||
|
options: { external: true, direction: 'forward' }, |
||||
|
}); |
||||
|
expect(events).toHaveLength(0); |
||||
|
}); |
||||
|
|
||||
|
test('Undo and redo apply reverse/forward changes', () => { |
||||
|
const calls = []; |
||||
|
const events = []; |
||||
|
const pm = new PatchManager({ |
||||
|
enabled: true, |
||||
|
applyPatch: (changes, options) => calls.push({ changes, options }), |
||||
|
emitter: { |
||||
|
trigger: (event) => events.push(event), |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const patch = { |
||||
|
id: 'patch-2', |
||||
|
changes: [{ op: 'replace', path: ['value'], value: 2 }], |
||||
|
reverseChanges: [{ op: 'replace', path: ['value'], value: 1 }], |
||||
|
}; |
||||
|
|
||||
|
pm.add(patch); |
||||
|
|
||||
|
const undoPatch = pm.undo(); |
||||
|
const redoPatch = pm.redo(); |
||||
|
|
||||
|
expect(undoPatch).toBe(patch); |
||||
|
expect(redoPatch).toBe(patch); |
||||
|
expect(calls[0]).toEqual({ changes: patch.reverseChanges, options: { direction: 'backward' } }); |
||||
|
expect(calls[1]).toEqual({ changes: patch.changes, options: { direction: 'forward' } }); |
||||
|
expect(events).toEqual([PatchManagerEvents.update, PatchManagerEvents.undo, PatchManagerEvents.redo]); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,92 @@ |
|||||
|
import PatchManager, { PatchManagerEvents } from 'patch_manager'; |
||||
|
import ModelWithPatches from 'patch_manager/ModelWithPatches'; |
||||
|
|
||||
|
describe('ModelWithPatches', () => { |
||||
|
test('set records patch with normalized path', async () => { |
||||
|
const events = []; |
||||
|
const pm = new PatchManager({ |
||||
|
enabled: true, |
||||
|
emitter: { |
||||
|
trigger: (event, payload) => events.push({ event, payload }), |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const model = new ModelWithPatches({ id: 'model-1', foo: 'bar' }); |
||||
|
model.em = { Patches: pm }; |
||||
|
model.patchObjectType = 'model'; |
||||
|
|
||||
|
model.set('foo', 'baz'); |
||||
|
|
||||
|
await Promise.resolve(); |
||||
|
|
||||
|
expect(events).toHaveLength(1); |
||||
|
expect(events[0].event).toBe(PatchManagerEvents.update); |
||||
|
|
||||
|
const patch = events[0].payload; |
||||
|
expect(patch.changes).toHaveLength(1); |
||||
|
expect(patch.reverseChanges).toHaveLength(1); |
||||
|
expect(patch.changes[0]).toMatchObject({ |
||||
|
op: 'replace', |
||||
|
path: ['model', 'model-1', 'attributes', 'foo'], |
||||
|
value: 'baz', |
||||
|
}); |
||||
|
expect(patch.reverseChanges[0]).toMatchObject({ |
||||
|
op: 'replace', |
||||
|
path: ['model', 'model-1', 'attributes', 'foo'], |
||||
|
value: 'bar', |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
test('set skips patch recording without a patch object type', async () => { |
||||
|
const events = []; |
||||
|
const pm = new PatchManager({ |
||||
|
enabled: true, |
||||
|
emitter: { |
||||
|
trigger: (event, payload) => events.push({ event, payload }), |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const model = new ModelWithPatches({ id: 'model-2', foo: 'bar' }); |
||||
|
model.em = { Patches: pm }; |
||||
|
|
||||
|
model.set('foo', 'baz'); |
||||
|
|
||||
|
await Promise.resolve(); |
||||
|
|
||||
|
expect(model.get('foo')).toBe('baz'); |
||||
|
expect(events).toHaveLength(0); |
||||
|
}); |
||||
|
|
||||
|
test('apply handler changes do not create patches while tracking is suppressed', async () => { |
||||
|
const events = []; |
||||
|
let model; |
||||
|
|
||||
|
const pm = new PatchManager({ |
||||
|
enabled: true, |
||||
|
emitter: { |
||||
|
trigger: (event, payload) => events.push({ event, payload }), |
||||
|
}, |
||||
|
applyPatch: () => { |
||||
|
model.set('foo', 'applied'); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
model = new ModelWithPatches({ id: 'model-3', foo: 'bar' }); |
||||
|
model.em = { Patches: pm }; |
||||
|
model.patchObjectType = 'model'; |
||||
|
|
||||
|
pm.apply( |
||||
|
{ |
||||
|
id: 'patch-3', |
||||
|
changes: [{ op: 'replace', path: ['model', 'model-3', 'attributes', 'foo'], value: 'applied' }], |
||||
|
reverseChanges: [{ op: 'replace', path: ['model', 'model-3', 'attributes', 'foo'], value: 'bar' }], |
||||
|
}, |
||||
|
{ external: true }, |
||||
|
); |
||||
|
|
||||
|
await Promise.resolve(); |
||||
|
|
||||
|
expect(model.get('foo')).toBe('applied'); |
||||
|
expect(events).toHaveLength(0); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue