From 92b56e59ecea7a2b854d4c7dc7a117f1e9f2619f Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Thu, 11 Nov 2021 23:05:02 +0900 Subject: [PATCH] Refactor tests --- .../use-yjs/__test-helpers__/utils.js | 49 ++++++++ .../use-yjs/__tests__/index.js | 106 +----------------- .../use-yjs/__tests__/undo.js | 76 +++++++++++++ 3 files changed, 126 insertions(+), 105 deletions(-) create mode 100644 src/components/collaborative-editing/use-yjs/__test-helpers__/utils.js create mode 100644 src/components/collaborative-editing/use-yjs/__tests__/undo.js diff --git a/src/components/collaborative-editing/use-yjs/__test-helpers__/utils.js b/src/components/collaborative-editing/use-yjs/__test-helpers__/utils.js new file mode 100644 index 000000000..fc68807d8 --- /dev/null +++ b/src/components/collaborative-editing/use-yjs/__test-helpers__/utils.js @@ -0,0 +1,49 @@ +/** + * Helper to generate mock transport modules for an isolated channel. + * + * @param {number} count - Number of transport modules to generate. + * @return {Object[]} - Array of transport modules. + */ +export function getTransports( count ) { + const peers = {}; + + const getAvailablePeers = ( excludeId ) => + Object.values( peers ).reduce( ( acc, p ) => { + if ( p.user.id === excludeId ) return acc; + return [ ...acc, p.user ]; + }, [] ); + + const mockTransport = () => { + const transport = { + _identity: undefined, + connect: jest.fn( ( { onReceiveMessage, setAvailablePeers, user } ) => { + transport._identity = user.identity; + peers[ user.identity ] = { onReceiveMessage, setAvailablePeers, user: { ...user, id: user.identity } }; + Object.keys( peers ).forEach( ( identity ) => { + peers[ identity ].setAvailablePeers( getAvailablePeers( identity ) ); + } ); + return Promise.resolve( { isFirstInChannel: Object.keys( peers ).length === 1 } ); + } ), + sendMessage: jest.fn( ( data ) => { + // console.log( data.message.messageType, data.identity.substring( 0, 4 ) ); + Object.keys( peers ).forEach( ( identity ) => { + if ( identity !== data.identity ) { + peers[ identity ].onReceiveMessage( data ); + } + } ); + } ), + disconnect: jest.fn( () => { + delete peers[ transport._identity ]; + Object.keys( peers ).forEach( ( identity ) => { + peers[ identity ].setAvailablePeers( getAvailablePeers( identity ) ); + } ); + } ), + }; + + return transport; + }; + + return Array( count ) + .fill( null ) + .map( () => mockTransport() ); +} diff --git a/src/components/collaborative-editing/use-yjs/__tests__/index.js b/src/components/collaborative-editing/use-yjs/__tests__/index.js index 9800a2d87..e4a37ff62 100644 --- a/src/components/collaborative-editing/use-yjs/__tests__/index.js +++ b/src/components/collaborative-editing/use-yjs/__tests__/index.js @@ -8,52 +8,7 @@ import userEvent from '@testing-library/user-event'; * Internal dependencies */ import IsolatedBlockEditor, { CollaborativeEditing } from '../../../../index'; - -/** - * Helper to generate mock transport modules for an isolated channel. - * - * @param {number} count - Number of transport modules to generate. - * @return {Object[]} - Array of transport modules. - */ -function getTransports( count ) { - const peers = {}; - - const getAvailablePeers = ( excludeId ) => - Object.values( peers ).reduce( ( acc, p ) => { - if ( p.user.id === excludeId ) return acc; - return [ ...acc, p.user ]; - }, [] ); - - const mockTransport = () => ( { - _identity: undefined, - connect: jest.fn( ( { onReceiveMessage, setAvailablePeers, user } ) => { - mockTransport._identity = user.identity; - peers[ user.identity ] = { onReceiveMessage, setAvailablePeers, user: { ...user, id: user.identity } }; - Object.keys( peers ).forEach( ( identity ) => { - peers[ identity ].setAvailablePeers( getAvailablePeers( identity ) ); - } ); - return Promise.resolve( { isFirstInChannel: Object.keys( peers ).length === 1 } ); - } ), - sendMessage: jest.fn( ( data ) => { - // console.log( data.message.messageType, data.identity.substring( 0, 4 ) ); - Object.keys( peers ).forEach( ( identity ) => { - if ( identity !== data.identity ) { - peers[ identity ].onReceiveMessage( data ); - } - } ); - } ), - disconnect: jest.fn( () => { - delete peers[ mockTransport._identity ]; - Object.keys( peers ).forEach( ( identity ) => { - peers[ identity ].setAvailablePeers( getAvailablePeers( identity ) ); - } ); - } ), - } ); - - return Array( count ) - .fill( null ) - .map( () => mockTransport() ); -} +import { getTransports } from '../__test-helpers__/utils'; const collabSettings = { enabled: true, @@ -211,62 +166,3 @@ describe( 'CollaborativeEditing', () => { expect( bobScreen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'initialtyped' ); } ); } ); - -describe( 'CollaborativeEditing: Undo/Redo', () => { - beforeEach( () => { - // Real timers are used so Yjs can merge undo stack items - jest.useRealTimers(); - } ); - - afterEach( () => { - jest.useFakeTimers(); - } ); - - it( 'should undo/redo single user edits', async () => { - const [ transport ] = getTransports( 1 ); - const onSave = jest.fn(); - - render( - - - - ); - - expect( screen.getByRole( 'button', { name: 'Undo' } ) ).toHaveAttribute( 'aria-disabled', 'true' ); - expect( screen.getByRole( 'button', { name: 'Redo' } ) ).toHaveAttribute( 'aria-disabled', 'true' ); - - userEvent.click( screen.getByText( /^Start writing.+/ ) ); - - 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 ) ) ); - userEvent.click( screen.getByRole( 'document', { name: 'Paragraph block' } ) ); - - userEvent.keyboard( 'world' ); - expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'helloworld' ); - - expect( screen.getByRole( 'button', { name: 'Undo' } ) ).toHaveAttribute( 'aria-disabled', 'false' ); - userEvent.click( screen.getByRole( 'button', { name: 'Undo' } ) ); - - expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'hello' ); - - expect( screen.getByRole( 'button', { name: 'Undo' } ) ).toHaveAttribute( 'aria-disabled', 'false' ); - expect( screen.getByRole( 'button', { name: 'Redo' } ) ).toHaveAttribute( 'aria-disabled', 'false' ); - userEvent.click( screen.getByRole( 'button', { name: 'Undo' } ) ); - - expect( screen.getByText( /^Start writing.+/ ) ).toBeInTheDocument(); - expect( onSave ).toHaveBeenLastCalledWith( '' ); - - expect( screen.getByRole( 'button', { name: 'Undo' } ) ).toHaveAttribute( 'aria-disabled', 'true' ); - expect( screen.getByRole( 'button', { name: 'Redo' } ) ).toHaveAttribute( 'aria-disabled', 'false' ); - - userEvent.click( screen.getByRole( 'button', { name: 'Redo' } ) ); - expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'hello' ); - - userEvent.click( screen.getByRole( 'button', { name: 'Redo' } ) ); - expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'helloworld' ); - } ); -} ); diff --git a/src/components/collaborative-editing/use-yjs/__tests__/undo.js b/src/components/collaborative-editing/use-yjs/__tests__/undo.js new file mode 100644 index 000000000..7bed5c965 --- /dev/null +++ b/src/components/collaborative-editing/use-yjs/__tests__/undo.js @@ -0,0 +1,76 @@ +/** + * External dependencies + */ +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +/** + * Internal dependencies + */ +import IsolatedBlockEditor, { CollaborativeEditing } from '../../../../index'; +import { getTransports } from '../__test-helpers__/utils'; + +const collabSettings = { + enabled: true, + username: 'Peery Peerson', + avatarUrl: 'fake-image.jpg', +}; + +describe( 'CollaborativeEditing: Undo/Redo', () => { + beforeEach( () => { + // Real timers are used so Yjs can merge undo stack items + jest.useRealTimers(); + } ); + + afterEach( () => { + jest.useFakeTimers(); + } ); + + it( 'should undo/redo single user edits', async () => { + const [ transport ] = getTransports( 1 ); + const onSave = jest.fn(); + + render( + + + + ); + + expect( screen.getByRole( 'button', { name: 'Undo' } ) ).toHaveAttribute( 'aria-disabled', 'true' ); + expect( screen.getByRole( 'button', { name: 'Redo' } ) ).toHaveAttribute( 'aria-disabled', 'true' ); + + userEvent.click( screen.getByText( /^Start writing.+/ ) ); + + 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 ) ) ); + userEvent.click( screen.getByRole( 'document', { name: 'Paragraph block' } ) ); + + userEvent.keyboard( 'world' ); + expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'helloworld' ); + + expect( screen.getByRole( 'button', { name: 'Undo' } ) ).toHaveAttribute( 'aria-disabled', 'false' ); + userEvent.click( screen.getByRole( 'button', { name: 'Undo' } ) ); + + expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'hello' ); + + expect( screen.getByRole( 'button', { name: 'Undo' } ) ).toHaveAttribute( 'aria-disabled', 'false' ); + expect( screen.getByRole( 'button', { name: 'Redo' } ) ).toHaveAttribute( 'aria-disabled', 'false' ); + userEvent.click( screen.getByRole( 'button', { name: 'Undo' } ) ); + + expect( screen.getByText( /^Start writing.+/ ) ).toBeInTheDocument(); + expect( onSave ).toHaveBeenLastCalledWith( '' ); + + expect( screen.getByRole( 'button', { name: 'Undo' } ) ).toHaveAttribute( 'aria-disabled', 'true' ); + expect( screen.getByRole( 'button', { name: 'Redo' } ) ).toHaveAttribute( 'aria-disabled', 'false' ); + + userEvent.click( screen.getByRole( 'button', { name: 'Redo' } ) ); + expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'hello' ); + + userEvent.click( screen.getByRole( 'button', { name: 'Redo' } ) ); + expect( screen.getByRole( 'document', { name: 'Paragraph block' } ) ).toHaveTextContent( 'helloworld' ); + } ); +} );