From 7859989c991a38ce7ffb6966acfed3f2f23f522c Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Fri, 12 Nov 2021 04:35:04 +0900 Subject: [PATCH] Add test for multiplayer undo --- .../use-yjs/__test-helpers__/utils.js | 17 +++++ .../use-yjs/__tests__/undo.js | 68 +++++++++++++++++-- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/components/collaborative-editing/use-yjs/__test-helpers__/utils.js b/src/components/collaborative-editing/use-yjs/__test-helpers__/utils.js index fc68807d8..3b47634f5 100644 --- a/src/components/collaborative-editing/use-yjs/__test-helpers__/utils.js +++ b/src/components/collaborative-editing/use-yjs/__test-helpers__/utils.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { waitFor } from '@testing-library/react'; + /** * Helper to generate mock transport modules for an isolated channel. * @@ -47,3 +52,15 @@ export function getTransports( count ) { .fill( null ) .map( () => mockTransport() ); } + +/** + * Emulate a pause between typing events for Yjs to make a new undo stack item. + * + * Requires jest.useRealTimers(). + * + * @param {import('@testing-library/dom').Screen} screen + */ +export async function pauseTyping( screen ) { + screen.getAllByRole( 'document' ).forEach( ( el ) => el.blur() ); + await waitFor( () => new Promise( ( resolve ) => setTimeout( resolve, 550 ) ) ); +} diff --git a/src/components/collaborative-editing/use-yjs/__tests__/undo.js b/src/components/collaborative-editing/use-yjs/__tests__/undo.js index 7bed5c965..1870aff56 100644 --- a/src/components/collaborative-editing/use-yjs/__tests__/undo.js +++ b/src/components/collaborative-editing/use-yjs/__tests__/undo.js @@ -1,14 +1,14 @@ /** * External dependencies */ -import { render, screen, waitFor } from '@testing-library/react'; +import { act, render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; /** * Internal dependencies */ import IsolatedBlockEditor, { CollaborativeEditing } from '../../../../index'; -import { getTransports } from '../__test-helpers__/utils'; +import { getTransports, pauseTyping } from '../__test-helpers__/utils'; const collabSettings = { enabled: true, @@ -44,9 +44,8 @@ describe( 'CollaborativeEditing: Undo/Redo', () => { userEvent.keyboard( 'hello' ); expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'hello' ); - // Emulate pause for Yjs to make a new undo stack item - screen.getByRole( 'document', { name: 'Paragraph block' } ).blur(); - await waitFor( () => new Promise( ( resolve ) => setTimeout( resolve, 500 ) ) ); + await pauseTyping( screen ); + userEvent.click( screen.getByRole( 'document', { name: 'Paragraph block' } ) ); userEvent.keyboard( 'world' ); @@ -73,4 +72,63 @@ describe( 'CollaborativeEditing: Undo/Redo', () => { userEvent.click( screen.getByRole( 'button', { name: 'Redo' } ) ); expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'helloworld' ); } ); + + it( 'should only undo/redo own edits', async () => { + const [ transport1, transport2 ] = getTransports( 2 ); + const [ onSave1, onSave2 ] = [ jest.fn(), jest.fn() ]; + await act( async () => { + render( + <> +
+ + + +
+
+ + + +
+ + ); + } ); + const aliceScreen = within( screen.getByTestId( 'alice' ) ); + const bobScreen = within( screen.getByTestId( 'bob' ) ); + + userEvent.click( aliceScreen.getByText( /^Start writing.+/ ) ); + userEvent.keyboard( 'alice:{Enter}' ); + + await pauseTyping( aliceScreen ); + + userEvent.click( bobScreen.getAllByRole( 'document' )[ 1 ] ); + userEvent.keyboard( 'bobby:' ); + + userEvent.click( bobScreen.getByRole( 'button', { name: 'Undo' } ) ); + expect( screen.queryByText( 'bobby:' ) ).toBe( null ); + + userEvent.click( bobScreen.getAllByRole( 'document' )[ 1 ] ); + userEvent.keyboard( 'bob:' ); + + await pauseTyping( bobScreen ); + + userEvent.click( aliceScreen.getAllByRole( 'document' )[ 0 ] ); + userEvent.keyboard( 'hello' ); + + userEvent.click( bobScreen.getByRole( 'button', { name: 'Undo' } ) ); + expect( screen.queryByText( 'bob:' ) ).toBe( null ); + + expect( screen.getByText( 'alice:hello' ) ).toBeInTheDocument(); + userEvent.click( aliceScreen.getByRole( 'button', { name: 'Undo' } ) ); + expect( screen.queryByText( 'hello' ) ).toBe( null ); + + userEvent.click( bobScreen.getByRole( 'button', { name: 'Redo' } ) ); + expect( screen.getByText( 'bob:' ) ).toBeInTheDocument(); + + // Both editors should have the same content + expect( onSave1 ).toHaveBeenLastCalledWith( onSave2.mock.calls[ onSave2.mock.calls.length - 1 ][ 0 ] ); + } ); } );