From c9508aa89a61c1330ad17fb86c9bbd281fd3ab48 Mon Sep 17 00:00:00 2001 From: Sion612 <167093684+Sion612@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:57:28 +0800 Subject: [PATCH] fix(saver): propagate save failures to callers --- src/components/modules/saver.ts | 2 + test/cypress/tests/modules/Saver.cy.ts | 80 ++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/components/modules/saver.ts b/src/components/modules/saver.ts index c021928350..ad21dc6a6c 100644 --- a/src/components/modules/saver.ts +++ b/src/components/modules/saver.ts @@ -44,6 +44,8 @@ export default class Saver extends Module { return this.makeOutput(sanitizedData); } catch (e) { _.logLabeled(`Saving failed due to the Error %o`, 'error', e); + + throw e; } } diff --git a/test/cypress/tests/modules/Saver.cy.ts b/test/cypress/tests/modules/Saver.cy.ts index dcebe6f5e7..ab192591e0 100644 --- a/test/cypress/tests/modules/Saver.cy.ts +++ b/test/cypress/tests/modules/Saver.cy.ts @@ -1,8 +1,88 @@ import type EditorJS from '../../../../types/index'; import Header from '@editorjs/header'; +import ToolMock from '../../fixtures/tools/ToolMock'; describe('Saver module', function () { describe('save()', function () { + ['throws', 'rejects'].forEach((failureMode) => { + it(`should reject with the original error when a tool's save() ${failureMode}`, function () { + const saveError = new Error('Tool failed to save'); + const save = cy.stub(ToolMock.prototype, 'save'); + + if (failureMode === 'throws') { + save.throws(saveError); + } else { + save.rejects(saveError); + } + + cy.createEditor({ + tools: { + failingTool: ToolMock, + }, + data: { + blocks: [ + { + type: 'failingTool', + data: { text: 'Unsaved content' }, + }, + ], + }, + }).then((editor: EditorJS) => { + return editor.save().then( + (data) => { + assert.fail(`Expected save() to reject, but it resolved with ${JSON.stringify(data)}`); + }, + (error) => { + expect(error).to.equal(saveError); + } + ); + }); + }); + }); + + it('should skip invalid blocks and save valid blocks', function () { + cy.createEditor({ + data: { + blocks: [ + { + type: 'paragraph', + data: { text: '' }, + }, + { + id: 'valid-block', + type: 'paragraph', + data: { text: 'Valid content' }, + }, + ], + }, + }).then(async (editor: EditorJS) => { + const data = await editor.save(); + + expect(data.blocks).to.deep.equal([ + { + id: 'valid-block', + type: 'paragraph', + data: { text: 'Valid content' }, + }, + ]); + }); + }); + + it('should still reject saving in read-only mode', function () { + cy.createEditor({ + readOnly: true, + }).then((editor: EditorJS) => { + return editor.save().then( + () => { + assert.fail('Expected saving in read-only mode to reject'); + }, + (error) => { + expect(error.message).to.equal('Editor\'s content can not be saved in read-only mode'); + } + ); + }); + }); + it('should correctly save block if there are some 3rd party (eg. browser extensions) nodes inserted into the layout', function () { cy.createEditor({ data: {