test(canvas): verify crash cuts and pending image replacement lifetimes

This commit is contained in:
2026-11-11 19:05:33 +08:00
parent dcbbdad25d
commit d3eef78431
4 changed files with 126 additions and 6 deletions
+14
View File
@@ -223,6 +223,20 @@ test('lost upload acknowledgment recovers original byte identity and query recei
await recovered.close();
});
test('replacing an image keeps the old server reference during upload and undo/reopen/redo never uploads again',async()=>{
const f=fixture();let engine=await f.open();const original=addImage(engine);await engine.settle();const upload=f.assets.upload,gate=deferred(),entered=deferred();let replacement;
f.assets.upload=async(...args)=>{entered.resolve();await gate.promise;return upload(...args);};
try{
const result=engine.stageFiles([{name:'replacement.png',bytes:new Blob(['different image bytes'],{type:'image/png'})}],([ref],tx)=>{replacement=ref;tx.dispatch({type:'element.data',target:{id:'picture',incarnation:1},patch:{assetId:ref.id,src:ref.src}});});assert.equal(result.status,'applied');
assert.equal(engine.getSnapshot().document.elements.get('picture').data.assetId,replacement.id);assert.equal(engine.getSnapshot().history.undo.length,2);await entered.promise;
assert.equal(f.writes.length,1);assert.equal(f.persistence.canonical(f.writes[0]).document.elements.get('picture').data.assetId,original.id);
assert.equal(engine.editor.history.undo().status,'applied');assert.equal(engine.getSnapshot().document.elements.get('picture').data.assetId,original.id);gate.resolve();await engine.settle();
const last=()=>f.persistence.canonical(f.writes.at(-1)).document.elements.get('picture');assert.equal(last().data.assetId,original.id);assert.equal(last().incarnation,1);assert.equal(f.uploads.length,2);assert.equal(engine.getSnapshot().history.undo.length,1);
assert.equal(engine.editor.history.redo().status,'applied');await engine.settle();assert.equal(last().data.assetId,replacement.id);assert.equal(f.uploads.length,2);assert.equal(engine.getSnapshot().history.undo.length,2);assert.ok(engine.getResourcePins().pins.has(original.id));
await engine.close();engine=await f.open();assert.equal(engine.editor.history.undo().status,'applied');await engine.settle();assert.equal(last().data.assetId,original.id);assert.equal(engine.editor.history.redo().status,'applied');await engine.settle();assert.equal(last().data.assetId,replacement.id);assert.equal(f.uploads.length,2);assert.equal(last().incarnation,1);
}finally{gate.resolve();await engine.close();}
});
test('rejected and empty staging do not retain file bytes or create upload work', async () => {
const f = fixture(), engine = await f.open();
const files = [{ name: 'image.png', bytes: new Blob(['image'], { type: 'image/png' }) }];