diff --git a/client/src/components/About/index.jsx b/client/src/components/About/index.jsx index effe95f..5243551 100644 --- a/client/src/components/About/index.jsx +++ b/client/src/components/About/index.jsx @@ -1,5 +1,5 @@ /* eslint-disable */ -import Reactfrom 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { COMMIT_SHA } from '@/config/env'; diff --git a/client/src/components/Chat/Chat.jsx b/client/src/components/Chat/Chat.jsx index a986b90..ad7f15d 100644 --- a/client/src/components/Chat/Chat.jsx +++ b/client/src/components/Chat/Chat.jsx @@ -140,7 +140,8 @@ export const Chat = ({ sendEncryptedMessage, showNotice, userId, username, clear return null; }; - const handleSendClick = () => { + const handleSendClick = evt => { + evt.preventDefault(); sendMessage(); textInputRef.current.focus(); }; diff --git a/client/src/components/Chat/Chat.test.jsx b/client/src/components/Chat/Chat.test.jsx index f38527d..70cfbff 100644 --- a/client/src/components/Chat/Chat.test.jsx +++ b/client/src/components/Chat/Chat.test.jsx @@ -207,7 +207,7 @@ describe('Chat component', () => { fireEvent.keyDown(textarea, { key: 'Enter' }); }); - it('should work with touch support', () => { + it('should work with touch support', async () => { // Enable touch support dom.hasTouchSupport = true; @@ -239,15 +239,15 @@ describe('Chat component', () => { fireEvent.change(textarea, { target: { value: 'test' } }); // Touch send button - fireEvent.click(getByTitle('Send')); + await fireEvent.click(getByTitle('Send')); expect(sendEncryptedMessage).toHaveBeenLastCalledWith({ payload: { text: 'test', timestamp: 1584183718135 }, type: 'TEXT_MESSAGE', }); - // Should not send message - fireEvent.click(getByTitle('Send')); + // Should not send message because of the empty message + await fireEvent.click(getByTitle('Send')); expect(sendEncryptedMessage).toHaveBeenCalledTimes(1); }); diff --git a/client/src/components/Home/ActivityList.test.jsx b/client/src/components/Home/ActivityList.test.jsx index e6f84d3..c28bc2b 100644 --- a/client/src/components/Home/ActivityList.test.jsx +++ b/client/src/components/Home/ActivityList.test.jsx @@ -1,7 +1,7 @@ -import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { Provider } from 'react-redux'; import { describe, it, expect, vi } from 'vitest'; +import { act } from 'react-dom/test-utils'; import configureStore from '@/store'; @@ -58,21 +58,25 @@ describe('ActivityList component', () => { , ); - fireEvent.click(getByText('By using Darkwire, you are agreeing to our Acceptable Use Policy and Terms of Service')); - vi.runAllTimers(); + await fireEvent.click( + getByText('By using Darkwire, you are agreeing to our Acceptable Use Policy and Terms of Service'), + ); + await act(() => vi.runAllTimers()); expect(mockOpenModal.mock.calls[0][0]).toBe('About'); - vi.runAllTimers(); + await act(() => vi.runAllTimers()); }); - it('should focus chat', () => { + it('should focus chat', async () => { const { getByTestId } = render( , ); - fireEvent.click(getByTestId('main-div')); - vi.runAllTimers(); + + await fireEvent.click(getByTestId('main-div')); + + await act(() => vi.runAllTimers()); }); it('should scroll to bottom on new message if not scrolled', () => { diff --git a/client/src/components/Home/Home.jsx b/client/src/components/Home/Home.jsx index 36f1083..b3f7970 100644 --- a/client/src/components/Home/Home.jsx +++ b/client/src/components/Home/Home.jsx @@ -290,7 +290,7 @@ const Home = ({ ); }; -const User = ({ createUser, username, ...rest }) => { +export const WithUser = ({ createUser, username, ...rest }) => { const [loaded, setLoaded] = React.useState(false); const loading = React.useRef(false); @@ -305,6 +305,7 @@ const User = ({ createUser, username, ...rest }) => { const exportedEncryptDecryptPublicKey = await crypto.exportKey(encryptDecryptKeys.publicKey); if (!mounted) { + loading.current = false; return; } @@ -313,15 +314,18 @@ const User = ({ createUser, username, ...rest }) => { publicKey: exportedEncryptDecryptPublicKey, privateKey: exportedEncryptDecryptPrivateKey, }); + + loading.current = false; setLoaded(true); }; - if (!loaded) { + if (!loaded && !loading.current) { loading.current = true; createUserLocal(); } return () => { + loading.current = false; mounted = false; }; }, [createUser, loaded, username]); @@ -332,11 +336,11 @@ const User = ({ createUser, username, ...rest }) => { return ; }; -User.defaultProps = { +WithUser.defaultProps = { modalComponent: null, }; -User.propTypes = { +WithUser.propTypes = { receiveEncryptedMessage: PropTypes.func.isRequired, receiveUnencryptedMessage: PropTypes.func.isRequired, createUser: PropTypes.func.isRequired, @@ -370,4 +374,4 @@ User.propTypes = { translations: PropTypes.object.isRequired, }; -export default User; +export default WithUser; diff --git a/client/src/components/Home/Home.test.jsx b/client/src/components/Home/Home.test.jsx index 4bbd275..213b73a 100644 --- a/client/src/components/Home/Home.test.jsx +++ b/client/src/components/Home/Home.test.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Provider } from 'react-redux'; import { render } from '@testing-library/react'; import { test, expect, vi } from 'vitest'; @@ -20,10 +19,18 @@ vi.mock('@/utils/socket', () => { return { on: vi.fn(), emit: vi.fn(), + close: vi.fn(), + }; + }), + getSocket: vi.fn().mockImplementation(() => { + return { + on: vi.fn(), + emit: vi.fn(), + close: vi.fn(), }; }), }; -}); // +}); vi.mock('../../utils/crypto', () => { // Need window.crytpo.subtle @@ -45,14 +52,13 @@ vi.mock('../../utils/crypto', () => { }); test('Home component is displaying', async () => { - const { asFragment } = render( + const { asFragment, findByText } = render( {}} activities={[]} - match={{ params: { roomId: 'roomTest' } }} createUser={() => {}} toggleSocketConnected={() => {}} receiveEncryptedMessage={() => {}} @@ -76,9 +82,16 @@ test('Home component is displaying', async () => { closeModal={() => {}} publicKey={{}} username={'linus'} + socketId={'roomTest'} + persistenceIsEnabled={false} + togglePersistenceEnabled={() => {}} + setLanguage={() => {}} + language={'en'} /> , ); + await findByText('Disconnected'); + expect(asFragment()).toMatchSnapshot(); }); diff --git a/client/src/components/Home/index.jsx b/client/src/components/Home/index.jsx index 9bd7aa8..c78ff94 100644 --- a/client/src/components/Home/index.jsx +++ b/client/src/components/Home/index.jsx @@ -65,14 +65,17 @@ const mapDispatchToProps = { export const ConnectedHome = connect(mapStateToProps, mapDispatchToProps)(Home); -const HomeWithParams = ({ ...props }) => { - const socketId = useLoaderData(); +export const ConnectedHomeWithNotification = ({ socketId, ...props }) => { return ( ); - // return ; +}; + +const HomeWithParams = ({ ...props }) => { + const socketId = useLoaderData(); + return ; }; export default HomeWithParams; diff --git a/client/src/components/Home/index.test.jsx b/client/src/components/Home/index.test.jsx index 80edc5c..a548ce3 100644 --- a/client/src/components/Home/index.test.jsx +++ b/client/src/components/Home/index.test.jsx @@ -8,11 +8,10 @@ import configureStore from '@/store'; import { toggleWindowFocus, toggleNotificationEnabled, toggleSoundEnabled } from '@/actions/app'; import { receiveEncryptedMessage } from '@/actions/encrypted_messages'; import { notify, beep } from '@/utils/notifications'; - -import { ConnectedHome } from './'; - import { act } from 'react-dom/test-utils'; +import { ConnectedHomeWithNotification } from './'; + const store = configureStore(); vi.useFakeTimers(); @@ -108,7 +107,7 @@ describe('Connected Home component', () => { it('should display', async () => { const { asFragment, findByText } = render( - + , ); @@ -117,10 +116,10 @@ describe('Connected Home component', () => { expect(asFragment()).toMatchSnapshot(); }); - it('should set notification', async () => { + it('should detect notification granted', async () => { const { findByText } = render( - + , ); @@ -128,53 +127,62 @@ describe('Connected Home component', () => { expect(store.getState().app.notificationIsAllowed).toBe(true); expect(store.getState().app.notificationIsEnabled).toBe(true); + }); + it('should detect notification denied', async () => { global.Notification = { permission: 'denied', }; - const { findByText: findByText2 } = render( + const { findByText } = render( - + , ); - await findByText2('Disconnected'); + await findByText('Disconnected'); expect(store.getState().app.notificationIsAllowed).toBe(false); + }); + it('should detect notification default', async () => { global.Notification = { permission: 'default', }; - const { findByText: findByText3 } = render( + const { findByText } = render( - + , ); - await findByText3('Disconnected'); + await findByText('Disconnected'); expect(store.getState().app.notificationIsAllowed).toBe(null); }); it('should send notifications', async () => { Modal.prototype.getSnapshotBeforeUpdate = vi.fn().mockReturnValue(null); - const { rerender } = render( + + const { rerender, findByText } = render( - + , ); + await findByText('Disconnected'); + // Test with window focused - await receiveEncryptedMessage({ - type: 'TEXT_MESSAGE', - payload: {}, - })(store.dispatch, store.getState); + await act(() => + receiveEncryptedMessage({ + type: 'TEXT_MESSAGE', + payload: {}, + })(store.dispatch, store.getState), + ); rerender( - + , ); @@ -184,15 +192,17 @@ describe('Connected Home component', () => { expect(Tinycon.setBubble).not.toHaveBeenCalled(); // Test with window unfocused - await toggleWindowFocus(false)(store.dispatch); - await receiveEncryptedMessage({ - type: 'TEXT_MESSAGE', - payload: {}, - })(store.dispatch, store.getState); + await act(() => toggleWindowFocus(false)(store.dispatch)); + await act(() => + receiveEncryptedMessage({ + type: 'TEXT_MESSAGE', + payload: {}, + })(store.dispatch, store.getState), + ); rerender( - + , ); expect(store.getState().app.unreadMessageCount).toBe(1); @@ -201,22 +211,24 @@ describe('Connected Home component', () => { expect(Tinycon.setBubble).toHaveBeenLastCalledWith(1); // Test with sound and notification disabled - await toggleNotificationEnabled(false)(store.dispatch); - await toggleSoundEnabled(false)(store.dispatch); - await receiveEncryptedMessage({ - type: 'TEXT_MESSAGE', - payload: {}, - })(store.dispatch, store.getState); + await act(() => toggleNotificationEnabled(false)(store.dispatch)); + await act(() => toggleSoundEnabled(false)(store.dispatch)); + await act(() => + receiveEncryptedMessage({ + type: 'TEXT_MESSAGE', + payload: {}, + })(store.dispatch, store.getState), + ); rerender( - + , ); expect(store.getState().app.unreadMessageCount).toBe(2); - expect(notify).toHaveBeenCalledTimes(1); - expect(beep.play).toHaveBeenCalledTimes(1); + expect(notify).toHaveBeenCalledTimes(2); + expect(beep.play).toHaveBeenCalledTimes(2); expect(Tinycon.setBubble).toHaveBeenLastCalledWith(2); }); }); diff --git a/client/src/reducers/room.js b/client/src/reducers/room.js index 2cbe8e1..7901d9e 100644 --- a/client/src/reducers/room.js +++ b/client/src/reducers/room.js @@ -50,7 +50,7 @@ const room = (state = initialState, action) => { return { ...state, members: [ - ...state.members, + ...state.members.filter(({ id })=> id !== action.payload.publicKey.n), { username: action.payload.username, publicKey: action.payload.publicKey,