test main chatbox functionality

This commit is contained in:
Sharon Kennedy 2020-03-13 00:05:12 -04:00
parent a97696f687
commit c798700a20
6 changed files with 207 additions and 93 deletions

View File

@ -1,11 +1,69 @@
export const mockCreateClient = jest.fn(); export const mockRegisterRequest = jest
export const mockStartClient = jest.fn(); .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(() => { export const mockLeave = jest.fn(() => {
return { return Promise.resolve('value');
createClient: mockCreateClient,
startClient: mockStartClient
};
}); });
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; 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)

View File

@ -108,6 +108,7 @@
"postcss-loader": "3.0.0", "postcss-loader": "3.0.0",
"sass-loader": "8.0.0", "sass-loader": "8.0.0",
"style-loader": "1.1.2", "style-loader": "1.1.2",
"wait-for-expect": "^3.0.2",
"webpack": "4.41.5", "webpack": "4.41.5",
"webpack-cli": "3.3.10", "webpack-cli": "3.3.10",
"webpack-dev-server": "3.10.1", "webpack-dev-server": "3.10.1",

View File

@ -430,3 +430,7 @@
height: calc(180px + 60vh); height: calc(180px + 60vh);
} }
} }
.hidden {
display: none;
}

View File

@ -120,16 +120,17 @@ class ChatBox extends React.Component {
} }
initializeChat = () => { initializeChat = () => {
// empty registration request to get session
this.setState({ ready: false }) this.setState({ ready: false })
let client; let client;
try { try {
client = matrix.createClient(this.props.matrixServerUrl) client = matrix.createClient(this.props.matrixServerUrl)
} catch { } catch(error) {
console.log("Error creating client", error)
return this.handleInitError() return this.handleInitError()
} }
// empty registration request to get session
return client.registerRequest({}) return client.registerRequest({})
.then(data => { .then(data => {
console.log("Empty registration request to get session", data) console.log("Empty registration request to get session", data)
@ -399,18 +400,12 @@ 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) { if (prevState.messages.length !== this.state.messages.length) {
if (this.messageWindow.current.scrollTo) {
this.messageWindow.current.scrollTo(0, this.messageWindow.current.scrollHeight) this.messageWindow.current.scrollTo(0, this.messageWindow.current.scrollHeight)
} }
} }
}
componentDidMount() { componentDidMount() {
document.addEventListener("keydown", this.handleEscape, false); document.addEventListener("keydown", this.handleEscape, false);
@ -424,7 +419,7 @@ class ChatBox extends React.Component {
} }
handleInputChange = e => { handleInputChange = e => {
this.setState({ inputValue: e.currentTarget.value }) this.setState({ inputValue: e.target.value })
} }
handleAcceptTerms = () => { handleAcceptTerms = () => {
@ -493,14 +488,13 @@ class ChatBox extends React.Component {
<div role="status">{typingStatus}</div> <div role="status">{typingStatus}</div>
</div> </div>
} }
{ { !ready && <div className={`loader`}>loading...</div> }
!ready && <div className="loader">loading...</div>
}
</div> </div>
</div> </div>
<div className="input-window"> <div className="input-window">
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<input <input
id="message-input"
type="text" type="text"
onChange={this.handleInputChange} onChange={this.handleInputChange}
value={inputValue} value={inputValue}
@ -509,7 +503,7 @@ class ChatBox extends React.Component {
autoFocus={true} autoFocus={true}
ref={this.chatboxInput} ref={this.chatboxInput}
/> />
<input type="submit" value="Send" /> <input type="submit" value="Send" id="submit" />
</form> </form>
</div> </div>
</div> </div>

View File

@ -1,9 +1,25 @@
import React from 'react'; import React from 'react';
import Chatbox from './chatbox'; 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 { mount, shallow } from 'enzyme';
import { createWaitForElement } from 'enzyme-wait'; import { createWaitForElement } from 'enzyme-wait';
import { config } from 'react-transition-group'; import { config } from 'react-transition-group';
import waitForExpect from 'wait-for-expect'
config.disabled = true config.disabled = true
@ -23,6 +39,21 @@ const testConfig = {
describe('Chatbox', () => { 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 () => { test('chat window should open and close', async () => {
const chatbox = mount(<Chatbox {...testConfig} />) const chatbox = mount(<Chatbox {...testConfig} />)
@ -61,107 +92,128 @@ describe('Chatbox', () => {
expect(messages.text()).toContain(props.agreementMessage) 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 () => { test('agreeing to terms should start encrypted chat', async () => {
const chatbox = mount(<Chatbox {...testConfig} />) const chatbox = mount(<Chatbox {...testConfig} />)
const dock = chatbox.find('button.dock') const dock = chatbox.find('button.dock')
dock.simulate('click') dock.simulate('click')
const yesButton = chatbox.find('#accept') const openChatWindow = await createWaitForElement('.widget-entered')(chatbox)
yesButton.simulate('click') 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 () => { test('rejecting terms should not start chat', async () => {
// const chatbox = mount(<Chatbox {...testConfig} />) const chatbox = mount(<Chatbox {...testConfig} />)
// const dock = chatbox.find('button.dock') const dock = chatbox.find('button.dock')
// dock.simulate('click') dock.simulate('click')
// const noButton = chatbox.find('#reject') const openChatWindow = await createWaitForElement('.widget-entered')(chatbox)
// noButton.simulate('click') 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', () => { expect(createClient.mock.calls.length).toEqual(0)
// handleInitError
}) })
test('#initializeChat should create unencypted chat if initCrypto fails', () => { test('notification should appear when facilitator joins chat', () => {
// initializeUnencryptedChat //
}) })
test('#initializeUnencryptedChat should initialize an unencrypted client', () => { test('submitted messages should be sent to matrix', async () => {
// initializeUnencryptedChat const chatbox = mount(<Chatbox {...testConfig} />)
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)
}) })
test('#handleDecryptionError should restart client without encryption and notify user', () => { form.simulate('submit')
// initializeUnencryptedChat
await waitForExpect(() => {
expect(mockSendTextMessage).toHaveBeenCalledWith(chatbox.state().roomId, message)
});
}) })
test('#verifyAllRoomDevices should mark all devices in the room as verified devices', () => { test('received messages should appear in chat window', () => {
//
}) })
test('#createRoom should create a new encrypted room with bot as admin', () => { test('decryption failure should lead to a new unencrypted chat', () => {
//
}) })
test('#createRoom should create a new unencrypted room if encryption is not enabled', () => { test('exiting the chat should leave the room and destroy client', async () => {
const chatbox = mount(<Chatbox {...testConfig} />)
const dock = chatbox.find('button.dock')
}) dock.simulate('click')
test('#sendMessage should send text message with input value', () => { 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 mark devices as known and retry sending on UnknownDeviceError', () => { 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('#displayFakeMessage should add a message object to message list', () => { await waitForExpect(() => {
expect(mockLeave).toHaveBeenCalled()
});
}) await waitForExpect(() => {
expect(mockDeactivateAccount).toHaveBeenCalled()
});
test('#displayBotMessage should add a message object with bot as sender to message list', () => { await waitForExpect(() => {
expect(mockStopClient).toHaveBeenCalled()
}) });
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(mockClearStores).toHaveBeenCalled()
});
}) })
}); });

View File

@ -12157,6 +12157,11 @@ w3c-hr-time@^1.0.1:
dependencies: dependencies:
browser-process-hrtime "^0.1.2" 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: walker@^1.0.7, walker@~1.0.5:
version "1.0.7" version "1.0.7"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"