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