
* refactor(bootstrap): add BootstrapShim * feat(security): [#209] generate public/private keys * refactor(encryption): move encryption utils to a service * feat(encryption): [wip] implement convertCryptoKeyToString * fix(user-settings): serialize crypto keys to strings * feat(user-settings): deserialize user settings from IndexedDB * feat(user-settings): upgrade persisted settings on boot * feat(user-settings): automatically migrate persisted user settings * refactor(encryption): simplify CryptoKey stringification * refactor(encryption): DRY up EncryptionService * feat(verification): send public key to new peers * refactor(encryption): use class instance * refactor(serialization): use class instance * refactor(verification): [wip] create usePeerVerification hook * feat(verification): encrypt verification token * feat(verification): send encrypted token to peer * feat(verification): verify peer * refactor(verification): use enum for verification state * feat(verification): expire verification requests * fix(updatePeer): update with fresh state data * feat(verification): display verification state * refactor(usePeerVerification): store verification timer in Peer * feat(verification): present tooltips explaining verification state * feat(ui): show full page loading indicator * feat(init): present bootup failure reasons * refactor(init): move init to its own file * feat(verification): show errors upon verification failure * refactor(verification): move workaround to usePeerVerification * feat(verification): present peer public keys * refactor(verification): move peer public key rendering to its own component * refactor(verification): only pass publicKey into renderer * feat(verification): show user's own public key * refactor(naming): rename Username to UserInfo * refactor(loading): encapsulate height styling * feat(verification): improve user messaging * refactor(style): improve formatting and variable names * feat(verification): add user info tooltip * docs(verification): explain verification
150 lines
3.7 KiB
TypeScript
150 lines
3.7 KiB
TypeScript
import { PropsWithChildren } from 'react'
|
|
import { waitFor, render, screen } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import { MemoryRouter as Router, Route, Routes } from 'react-router-dom'
|
|
|
|
import { userSettingsContextStubFactory } from 'test-utils/stubs/settingsContext'
|
|
import { mockEncryptionService } from 'test-utils/mocks/mockEncryptionService'
|
|
|
|
import { SettingsContext } from 'contexts/SettingsContext'
|
|
|
|
import { Room, RoomProps } from './'
|
|
|
|
const mockUserId = 'user-id'
|
|
const mockRoomId = 'room-123'
|
|
|
|
const userSettingsStub = userSettingsContextStubFactory({
|
|
userId: mockUserId,
|
|
})
|
|
|
|
window.AudioContext = jest.fn().mockImplementation()
|
|
const mockGetUuid = jest.fn()
|
|
const mockMessagedSender = jest
|
|
.fn()
|
|
.mockImplementation(() => Promise.resolve([]))
|
|
|
|
jest.mock('trystero', () => ({
|
|
joinRoom: () => ({
|
|
makeAction: () => [mockMessagedSender, () => {}, () => {}],
|
|
ping: () => Promise.resolve(0),
|
|
leave: () => {},
|
|
getPeers: () => [],
|
|
addStream: () => [Promise.resolve()],
|
|
removeStream: () => {},
|
|
addTrack: () => [Promise.resolve()],
|
|
removeTrack: () => {},
|
|
replaceTrack: () => [Promise.resolve()],
|
|
onPeerJoin: () => {},
|
|
onPeerLeave: () => {},
|
|
onPeerStream: () => {},
|
|
onPeerTrack: () => {},
|
|
}),
|
|
}))
|
|
|
|
const RouteStub = ({ children }: PropsWithChildren) => {
|
|
return (
|
|
<Router initialEntries={['/public/abc123']}>
|
|
<SettingsContext.Provider value={userSettingsStub}>
|
|
<Routes>
|
|
<Route path="/public/:roomId" element={children}></Route>
|
|
</Routes>
|
|
</SettingsContext.Provider>
|
|
</Router>
|
|
)
|
|
}
|
|
|
|
jest.useFakeTimers().setSystemTime(100)
|
|
|
|
const RoomStub = (props: RoomProps) => {
|
|
return <Room encryptionService={mockEncryptionService} {...props} />
|
|
}
|
|
|
|
describe('Room', () => {
|
|
test('is available', () => {
|
|
render(
|
|
<RouteStub>
|
|
<RoomStub userId={mockUserId} roomId={mockRoomId} />
|
|
</RouteStub>
|
|
)
|
|
})
|
|
|
|
test('send button is disabled', () => {
|
|
render(
|
|
<RouteStub>
|
|
<RoomStub userId={mockUserId} roomId={mockRoomId} />
|
|
</RouteStub>
|
|
)
|
|
|
|
const sendButton = screen.getByLabelText('Send')
|
|
expect(sendButton).toBeDisabled()
|
|
})
|
|
|
|
test('inputting text enabled send button', async () => {
|
|
render(
|
|
<RouteStub>
|
|
<RoomStub userId={mockUserId} roomId={mockRoomId} />
|
|
</RouteStub>
|
|
)
|
|
|
|
const sendButton = screen.getByLabelText('Send')
|
|
const textInput = screen.getByPlaceholderText('Your message')
|
|
|
|
await waitFor(() => {
|
|
userEvent.type(textInput, 'hello')
|
|
})
|
|
|
|
expect(sendButton).not.toBeDisabled()
|
|
})
|
|
|
|
test('sending a message clears the text input', async () => {
|
|
render(
|
|
<RouteStub>
|
|
<RoomStub userId={mockUserId} roomId={mockRoomId} />
|
|
</RouteStub>
|
|
)
|
|
|
|
const sendButton = screen.getByLabelText('Send')
|
|
const textInput = screen.getByPlaceholderText('Your message')
|
|
|
|
await waitFor(() => {
|
|
userEvent.type(textInput, 'hello')
|
|
})
|
|
|
|
await waitFor(() => {
|
|
userEvent.click(sendButton)
|
|
})
|
|
|
|
expect(textInput).toHaveValue('')
|
|
})
|
|
|
|
test('message is sent to peer', async () => {
|
|
render(
|
|
<RouteStub>
|
|
<RoomStub
|
|
getUuid={mockGetUuid.mockImplementation(() => 'abc123')}
|
|
userId={mockUserId}
|
|
roomId={mockRoomId}
|
|
/>
|
|
</RouteStub>
|
|
)
|
|
|
|
const sendButton = screen.getByLabelText('Send')
|
|
const textInput = screen.getByPlaceholderText('Your message')
|
|
|
|
await waitFor(() => {
|
|
userEvent.type(textInput, 'hello')
|
|
})
|
|
|
|
await waitFor(() => {
|
|
userEvent.click(sendButton)
|
|
})
|
|
|
|
expect(mockMessagedSender).toHaveBeenCalledWith({
|
|
authorId: mockUserId,
|
|
text: 'hello',
|
|
timeSent: 100,
|
|
id: 'abc123',
|
|
})
|
|
})
|
|
})
|