From df1131ec5d18471aba4d6e7a1ad93041bd5418e4 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 24 Jun 2024 18:10:54 +0200 Subject: [PATCH] fix(reconnect): keep baseVersionEtag during reconnect `this.$syncService` is cleared during the `close` method. However we need the `baseVersionEtag` to ensure the editing session on the server is still in sync with our local ydoc. Signed-off-by: Max --- cypress/e2e/sync.spec.js | 37 +++++++++++++++++++++++-------------- src/components/Editor.vue | 5 +++-- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/cypress/e2e/sync.spec.js b/cypress/e2e/sync.spec.js index 5c58ffb2905..d50b9d65b30 100644 --- a/cypress/e2e/sync.spec.js +++ b/cypress/e2e/sync.spec.js @@ -47,22 +47,11 @@ describe('Sync', () => { }) it('recovers from a short lost connection', () => { - let reconnect = false - cy.intercept('**/apps/text/session/*/*', (req) => { - if (reconnect) { - req.continue() - req.alias = 'alive' - } else { - req.destroy() - req.alias = 'dead' - } - }).as('sessionRequests') + cy.intercept('**/apps/text/session/*/*', req => req.destroy()).as('dead') cy.wait('@dead', { timeout: 30000 }) cy.get('#editor-container .document-status', { timeout: 30000 }) .should('contain', 'Document could not be loaded.') - .then(() => { - reconnect = true - }) + cy.intercept('**/apps/text/session/*/*', req => req.continue()).as('alive') cy.wait('@alive', { timeout: 30000 }) cy.intercept({ method: 'POST', url: '**/apps/text/session/*/sync' }) .as('syncAfterRecovery') @@ -80,6 +69,26 @@ describe('Sync', () => { .should('include', 'after the lost connection') }) + it('reconnects via button after a short lost connection', () => { + cy.intercept('**/apps/text/session/*/*', req => req.destroy()).as('dead') + cy.wait('@dead', { timeout: 30000 }) + cy.get('#editor-container .document-status', { timeout: 30000 }) + .should('contain', 'Document could not be loaded.') + cy.get('#editor-container .document-status') + .find('.button.primary').click() + cy.intercept('**/apps/text/session/*/*', req => { + if (req.url.endsWith('create')) { + req.alias = 'create' + } + req.continue() + }).as('alive') + cy.wait('@alive', { timeout: 30000 }) + cy.wait('@create', { timeout: 10000 }) + .its('request.body') + .should('have.property', 'baseVersionEtag') + .should('not.be.empty') + }) + it('recovers from a lost and closed connection', () => { let reconnect = false cy.intercept('**/apps/text/session/*/*', (req) => { @@ -111,7 +120,7 @@ describe('Sync', () => { .should('include', 'after the lost connection') }) - it('shows warning when document session got cleaned up', () => { + it('asks to reload page when document session got cleaned up', () => { cy.get('.save-status button') .click() cy.wait('@save') diff --git a/src/components/Editor.vue b/src/components/Editor.vue index ca28141f38d..929e786ef00 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -373,7 +373,7 @@ export default { guestName, shareToken: this.shareToken, filePath: this.relativePath, - baseVersionEtag: this.$syncService?.baseVersionEtag, + baseVersionEtag: this.$baseVersionEtag, forceRecreate: this.forceRecreate, serialize: this.isRichEditor ? (content) => createMarkdownSerializer(this.$editor.schema).serialize(content ?? this.$editor.state.doc) @@ -487,7 +487,7 @@ export default { }) }, - onLoaded({ documentSource, documentState }) { + onLoaded({ document, documentSource, documentState }) { if (documentState) { applyDocumentState(this.$ydoc, documentState, this.$providers[0]) // distribute additional state that may exist locally @@ -500,6 +500,7 @@ export default { this.setInitialYjsState(documentSource, { isRichEditor: this.isRichEditor }) } + this.$baseVersionEtag = document.baseVersionEtag this.hasConnectionIssue = false const language = extensionHighlight[this.fileExtension] || this.fileExtension;