Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #27 from ckeditor/i/2546
Browse files Browse the repository at this point in the history
Feature: Provided error handling for save actions. Closes ckeditor/ckeditor5#1546.
  • Loading branch information
Piotr Jasiun authored Oct 14, 2019
2 parents e28becf + 86843fe commit d6079bf
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/autosave.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ export default class Autosave extends Plugin {
* * synchronized – When all changes are saved.
* * waiting – When the plugin is waiting for other changes before calling `adapter#save()` and `config.autosave.save()`.
* * saving – When the provided save method is called and the plugin waits for the response.
* * error &ndash When the provided save method will throw an error. This state immediately changes to the `saving` state and
* the save method will be called again in the short period of time.
*
* @member {'synchronized'|'waiting'|'saving'} #state
*/
Expand Down Expand Up @@ -237,6 +239,18 @@ export default class Autosave extends Plugin {
.then( () => Promise.all(
this._saveCallbacks.map( cb => cb( this.editor ) )
) )
// In case of an error re-try the save later and throw the original error.
// Being in the `saving` state ensures that the debounced save action
// won't be delayed further by the `change:data` event listener.
.catch( err => {
this.state = 'error';
// Change immediately to the `saving` state so the `change:state` event will be fired.
this.state = 'saving';

this._debouncedSave();

throw err;
} )
.then( () => {
if ( this.editor.model.document.version > this._lastDocumentVersion ) {
this.state = 'waiting';
Expand Down
41 changes: 40 additions & 1 deletion tests/autosave.js
Original file line number Diff line number Diff line change
Expand Up @@ -617,13 +617,52 @@ describe( 'Autosave', () => {
expect( pendingActions.hasAny ).to.be.true;
sinon.clock.tick( 1000 );
} )
.then( () => Promise.resolve() )
.then( runPromiseCycles )
.then( () => {
expect( pendingActions.hasAny ).to.be.false;
sinon.assert.calledOnce( serverActionSpy );
sinon.assert.calledOnce( serverActionStub );
} );
} );

it( 'should handle a situration when the save callback throws an error', () => {
const pendingActions = editor.plugins.get( PendingActions );
const successServerActionSpy = sinon.spy();
const serverActionStub = sinon.stub();

serverActionStub.onFirstCall()
.rejects( new Error( 'foo' ) );

serverActionStub.onSecondCall()
.callsFake( successServerActionSpy );

autosave.adapter = {
save: serverActionStub
};

editor.model.change( writer => {
writer.setSelection( writer.createRangeIn( editor.model.document.getRoot().getChild( 0 ) ) );
editor.model.insertContent( writer.createText( 'foo' ) );
} );

return editor.destroy()
.then( () => {
expect( pendingActions.hasAny ).to.be.true;
} )
.then( runPromiseCycles )
.then( () => {
expect( pendingActions.hasAny ).to.be.true;
sinon.assert.calledOnce( serverActionStub );
sinon.assert.notCalled( successServerActionSpy );
} )
.then( () => sinon.clock.tick( 1000 ) )
.then( runPromiseCycles )
.then( () => {
expect( pendingActions.hasAny ).to.be.false;
sinon.assert.calledTwice( serverActionStub );
sinon.assert.calledOnce( successServerActionSpy );
} );
} );
} );

it( 'should run callbacks until the editor is in the ready state', () => {
Expand Down

0 comments on commit d6079bf

Please sign in to comment.