diff --git a/__mocks__/matrix-js-sdk.js b/__mocks__/matrix-js-sdk.js
index d6bd58a..6565450 100644
--- a/__mocks__/matrix-js-sdk.js
+++ b/__mocks__/matrix-js-sdk.js
@@ -1,11 +1,69 @@
-export const mockCreateClient = jest.fn();
-export const mockStartClient = jest.fn();
+export const mockRegisterRequest = jest
+ .fn()
+ .mockImplementation((params) => {
+ if (!params.auth) {
+ return Promise.reject({
+ data: { session: "session_id_1234" }
+ })
+ } else {
+ return Promise.resolve({
+ data: {
+ device_id: 'device_id_1234',
+ access_token: 'token_1234',
+ user_id: 'user_id_1234',
+ session: "session_id_1234"
+ }
+ })
+ }
+ })
-const mockMatrix = jest.fn().mockImplementation(() => {
- return {
- createClient: mockCreateClient,
- startClient: mockStartClient
- };
+export const mockLeave = jest.fn(() => {
+ return Promise.resolve('value');
});
+export const mockInitCrypto = jest.fn()
+export const mockStartClient = jest.fn(() => {
+ return Promise.resolve('value');
+});
+export const mockOnce = jest.fn()
+export const mockStopClient = jest.fn(() => {
+ return Promise.resolve('value');
+});
+export const mockClearStores = jest.fn(() => {
+ return Promise.resolve('value');
+});
+export const mockGetRoom = jest.fn()
+export const mockDownloadKeys = jest.fn()
+export const mockSetDeviceVerified = jest.fn()
+export const mockIsCryptoEnabled = jest.fn()
+export const mockCreateRoom = jest.fn().mockReturnValue({ room_id: 'room_id_1234' })
+export const mockSetPowerLevel = jest.fn()
+export const mockSendTextMessage = jest.fn(() => {
+ return Promise.resolve('value');
+});
+export const mockSetDeviceKnown = jest.fn()
+export const mockDeactivateAccount = jest.fn(() => {
+ return Promise.resolve('value');
+});
+export const mockOn = jest.fn()
-export default mockMatrix;
\ No newline at end of file
+export const mockClient = {
+ registerRequest: mockRegisterRequest,
+ initCrypto: mockInitCrypto,
+ startClient: mockStartClient,
+ on: mockOn,
+ once: mockOnce,
+ leave: mockLeave,
+ stopClient: mockStopClient,
+ clearStores: mockClearStores,
+ getRoom: mockGetRoom,
+ downloadKeys: mockDownloadKeys,
+ setDeviceVerified: mockSetDeviceVerified,
+ setDeviceKnown: mockSetDeviceKnown,
+ isCryptoEnabled: mockIsCryptoEnabled,
+ createRoom: mockCreateRoom,
+ setPowerLevel: mockSetPowerLevel,
+ sendTextMessage: mockSendTextMessage,
+ deactivateAccount: mockDeactivateAccount,
+}
+
+export const createClient = jest.fn().mockReturnValue(mockClient)
diff --git a/package.json b/package.json
index fdd314a..224f1f6 100644
--- a/package.json
+++ b/package.json
@@ -108,6 +108,7 @@
"postcss-loader": "3.0.0",
"sass-loader": "8.0.0",
"style-loader": "1.1.2",
+ "wait-for-expect": "^3.0.2",
"webpack": "4.41.5",
"webpack-cli": "3.3.10",
"webpack-dev-server": "3.10.1",
diff --git a/src/components/_chat.scss b/src/components/_chat.scss
index fe30bdc..8e983dc 100644
--- a/src/components/_chat.scss
+++ b/src/components/_chat.scss
@@ -430,3 +430,7 @@
height: calc(180px + 60vh);
}
}
+
+.hidden {
+ display: none;
+}
diff --git a/src/components/chatbox.jsx b/src/components/chatbox.jsx
index c01ac8d..beebf71 100644
--- a/src/components/chatbox.jsx
+++ b/src/components/chatbox.jsx
@@ -120,16 +120,17 @@ class ChatBox extends React.Component {
}
initializeChat = () => {
- // empty registration request to get session
this.setState({ ready: false })
let client;
try {
client = matrix.createClient(this.props.matrixServerUrl)
- } catch {
+ } catch(error) {
+ console.log("Error creating client", error)
return this.handleInitError()
}
+ // empty registration request to get session
return client.registerRequest({})
.then(data => {
console.log("Empty registration request to get session", data)
@@ -399,16 +400,10 @@ class ChatBox extends React.Component {
});
}
- if (!prevState.ready && this.state.ready) {
- this.chatboxInput.current.focus()
- }
-
- if (!prevState.opened && this.state.opened) {
- this.chatboxInput.current.focus()
- }
-
if (prevState.messages.length !== this.state.messages.length) {
- this.messageWindow.current.scrollTo(0, this.messageWindow.current.scrollHeight)
+ if (this.messageWindow.current.scrollTo) {
+ this.messageWindow.current.scrollTo(0, this.messageWindow.current.scrollHeight)
+ }
}
}
@@ -424,7 +419,7 @@ class ChatBox extends React.Component {
}
handleInputChange = e => {
- this.setState({ inputValue: e.currentTarget.value })
+ this.setState({ inputValue: e.target.value })
}
handleAcceptTerms = () => {
@@ -493,14 +488,13 @@ class ChatBox extends React.Component {
{typingStatus}
}
- {
- !ready && loading...
- }
+ { !ready && loading...
}
diff --git a/src/components/chatbox.test.js b/src/components/chatbox.test.js
index a84a36e..1deb6e9 100644
--- a/src/components/chatbox.test.js
+++ b/src/components/chatbox.test.js
@@ -1,9 +1,25 @@
import React from 'react';
import Chatbox from './chatbox';
-import mockMatrix, { mockCreateClient } from "matrix-js-sdk";
+import {
+ createClient,
+ mockClient,
+ mockRegisterRequest,
+ mockInitCrypto,
+ mockStartClient,
+ mockSetPowerLevel,
+ mockCreateRoom,
+ mockLeave,
+ mockDeactivateAccount,
+ mockStopClient,
+ mockClearStores,
+ mockOn,
+ mockOnce,
+ mockSendTextMessage
+} from "matrix-js-sdk";
import { mount, shallow } from 'enzyme';
import { createWaitForElement } from 'enzyme-wait';
import { config } from 'react-transition-group';
+import waitForExpect from 'wait-for-expect'
config.disabled = true
@@ -23,6 +39,21 @@ const testConfig = {
describe('Chatbox', () => {
+ beforeEach(() => {
+ createClient.mockClear()
+ mockInitCrypto.mockClear()
+ mockStartClient.mockClear()
+ mockRegisterRequest.mockClear()
+ mockSetPowerLevel.mockClear()
+ mockCreateRoom.mockClear()
+ mockLeave.mockClear()
+ mockDeactivateAccount.mockClear()
+ mockStopClient.mockClear()
+ mockClearStores.mockClear()
+ mockOnce.mockClear()
+ mockOn.mockClear()
+ })
+
test('chat window should open and close', async () => {
const chatbox = mount()
@@ -61,107 +92,128 @@ describe('Chatbox', () => {
expect(messages.text()).toContain(props.agreementMessage)
});
- test('#handleExitChat should call exitChat if the client has been initialized', () => {
-
- })
-
- test('#exitChat should leave the room and destroy client', () => {
- // leave room
- // deactivate account
- // stop client
- // clear stores
- // reset initial state
- })
-
test('agreeing to terms should start encrypted chat', async () => {
const chatbox = mount()
const dock = chatbox.find('button.dock')
dock.simulate('click')
- const yesButton = chatbox.find('#accept')
- yesButton.simulate('click')
+ const openChatWindow = await createWaitForElement('.widget-entered')(chatbox)
+ let acceptButton = await createWaitForElement('button#accept')(chatbox)
+ acceptButton = chatbox.find('button#accept')
- expect(mockCreateClient).toHaveBeenCalled()
+ acceptButton.simulate('click')
+
+ const ready = await createWaitForElement('.loader')(chatbox)
+ expect(ready.length).toEqual(1)
+
+ expect(createClient).toHaveBeenCalled()
+ expect(mockInitCrypto).toHaveBeenCalled()
+ expect(mockStartClient).toHaveBeenCalled()
+ expect(mockCreateRoom).toHaveBeenCalled()
+ expect(mockSetPowerLevel).toHaveBeenCalled()
+ expect(mockSetPowerLevel).toHaveBeenCalled()
+ expect(mockOn).toHaveBeenCalled()
+ expect(mockOnce).toHaveBeenCalled()
})
- // test('rejecting terms should not start chat', async () => {
- // const chatbox = mount()
- // const dock = chatbox.find('button.dock')
+ test('rejecting terms should not start chat', async () => {
+ const chatbox = mount()
+ const dock = chatbox.find('button.dock')
- // dock.simulate('click')
+ dock.simulate('click')
- // const noButton = chatbox.find('#reject')
- // noButton.simulate('click')
+ const openChatWindow = await createWaitForElement('.widget-entered')(chatbox)
+ let rejectButton = await createWaitForElement('button#reject')(chatbox)
+ rejectButton = chatbox.find('button#reject')
- // expect(mockMatrix.mockCreateClient.mock.calls.length).toEqual(0)
- // })
+ rejectButton.simulate('click')
- test('#initializeChat should notify user if client fails to initialize', () => {
- // handleInitError
+ expect(createClient.mock.calls.length).toEqual(0)
})
- test('#initializeChat should create unencypted chat if initCrypto fails', () => {
- // initializeUnencryptedChat
+ test('notification should appear when facilitator joins chat', () => {
+ //
})
- test('#initializeUnencryptedChat should initialize an unencrypted client', () => {
- // initializeUnencryptedChat
+ test('submitted messages should be sent to matrix', async () => {
+ const chatbox = mount()
+ const dock = chatbox.find('button.dock')
+
+ dock.simulate('click')
+
+ let acceptButton = await createWaitForElement('button#accept')(chatbox)
+ acceptButton = chatbox.find('button#accept')
+
+ acceptButton.simulate('click')
+
+ await waitForExpect(() => {
+ expect(mockCreateRoom).toHaveBeenCalled()
+ });
+
+ const input = chatbox.find('#message-input')
+ const form = chatbox.find('form')
+ const message = 'Hello'
+
+ input.simulate('change', { target: { value: message }})
+
+ await waitForExpect(() => {
+ chatbox.update()
+ expect(chatbox.state().inputValue).toEqual(message)
+ })
+
+ form.simulate('submit')
+
+ await waitForExpect(() => {
+ expect(mockSendTextMessage).toHaveBeenCalledWith(chatbox.state().roomId, message)
+ });
})
- test('#handleDecryptionError should restart client without encryption and notify user', () => {
- // initializeUnencryptedChat
+ test('received messages should appear in chat window', () => {
+ //
})
- test('#verifyAllRoomDevices should mark all devices in the room as verified devices', () => {
-
+ test('decryption failure should lead to a new unencrypted chat', () => {
+ //
})
- test('#createRoom should create a new encrypted room with bot as admin', () => {
+ test('exiting the chat should leave the room and destroy client', async () => {
+ const chatbox = mount()
+ const dock = chatbox.find('button.dock')
- })
+ dock.simulate('click')
- test('#createRoom should create a new unencrypted room if encryption is not enabled', () => {
+ const openChatWindow = await createWaitForElement('.widget-entered')(chatbox)
+ let acceptButton = await createWaitForElement('button#accept')(chatbox)
+ acceptButton = chatbox.find('button#accept')
- })
+ acceptButton.simulate('click')
- test('#sendMessage should send text message with input value', () => {
+ await waitForExpect(() => {
+ expect(mockCreateRoom).toHaveBeenCalled()
+ });
- })
+ const exitButton = chatbox.find('button.widget-header-close')
- test('#sendMessage should mark devices as known and retry sending on UnknownDeviceError', () => {
+ exitButton.simulate('click')
- })
+ let closed = await createWaitForElement('button.dock')
+ expect(closed.length).toEqual(1)
- test('#sendMessage should mark devices as known and retry sending on UnknownDeviceError', () => {
+ await waitForExpect(() => {
+ expect(mockLeave).toHaveBeenCalled()
+ });
- })
+ await waitForExpect(() => {
+ expect(mockDeactivateAccount).toHaveBeenCalled()
+ });
- test('#displayFakeMessage should add a message object to message list', () => {
-
- })
-
- test('#displayBotMessage should add a message object with bot as sender to message list', () => {
-
- })
-
- test('#handleMessageEvent should add received message to message list', () => {
-
- })
-
- test('#componentDidUpdate should set state listeners', () => {
-
- })
-
- test('#handleSubmit should listen for yes if awaiting agreement and initialize client', () => {
-
- })
-
- test('#handleSubmit should listen for no if awaiting agreement and do nothing', () => {
-
- })
-
- test('#handleSubmit should send message if awaitingAgreement is false', () => {
+ await waitForExpect(() => {
+ expect(mockStopClient).toHaveBeenCalled()
+ });
+ await waitForExpect(() => {
+ expect(mockClearStores).toHaveBeenCalled()
+ });
})
});
diff --git a/yarn.lock b/yarn.lock
index 0fcf7e4..b241199 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12157,6 +12157,11 @@ w3c-hr-time@^1.0.1:
dependencies:
browser-process-hrtime "^0.1.2"
+wait-for-expect@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-3.0.2.tgz#d2f14b2f7b778c9b82144109c8fa89ceaadaa463"
+ integrity sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag==
+
walker@^1.0.7, walker@~1.0.5:
version "1.0.7"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"