Merge pull request #23940 from overleaf/td-react-18
Upgrade to React 18 GitOrigin-RevId: 9b81936e6eea2bccd97fe5c2c5841f0b946371b8
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// Disable prop type checks for test harnesses
|
||||
/* eslint-disable react/prop-types */
|
||||
|
||||
import { renderHook, act } from '@testing-library/react-hooks/dom'
|
||||
import { renderHook, act, waitFor } from '@testing-library/react'
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import fetchMock from 'fetch-mock'
|
||||
@@ -30,6 +30,7 @@ describe('ChatContext', function () {
|
||||
|
||||
window.metaAttributesCache.set('ol-user', user)
|
||||
window.metaAttributesCache.set('ol-chatEnabled', true)
|
||||
window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
|
||||
|
||||
this.stub = sinon.stub(chatClientIdGenerator, 'generate').returns(uuidValue)
|
||||
})
|
||||
@@ -43,8 +44,9 @@ describe('ChatContext', function () {
|
||||
describe('socket connection', function () {
|
||||
beforeEach(function () {
|
||||
// Mock GET messages to return no messages
|
||||
// FIXME?
|
||||
// fetchMock.get('express:/project/:projectId/messages', [])
|
||||
fetchMock.get('express:/project/:projectId/messages', [], {
|
||||
name: 'fetchMessages',
|
||||
})
|
||||
|
||||
// Mock POST new message to return 200
|
||||
fetchMock.post('express:/project/:projectId/messages', 200)
|
||||
@@ -72,13 +74,15 @@ describe('ChatContext', function () {
|
||||
it('adds received messages to the list', async function () {
|
||||
// Mock socket: we only need to emit events, not mock actual connections
|
||||
const socket = new SocketIOMock()
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({
|
||||
const { result } = renderChatContextHook({
|
||||
socket,
|
||||
})
|
||||
|
||||
// Wait until initial messages have loaded
|
||||
result.current.loadInitialMessages()
|
||||
await waitForNextUpdate()
|
||||
await waitFor(
|
||||
() => expect(result.current.initialMessagesLoaded).to.be.true
|
||||
)
|
||||
|
||||
// No messages shown at first
|
||||
expect(result.current.messages).to.deep.equal([])
|
||||
@@ -95,30 +99,34 @@ describe('ChatContext', function () {
|
||||
},
|
||||
})
|
||||
|
||||
const message = result.current.messages[0]
|
||||
expect(message.id).to.equal('msg_1')
|
||||
expect(message.contents).to.deep.equal(['new message'])
|
||||
await waitFor(() => {
|
||||
const message = result.current.messages[0]
|
||||
expect(message.id).to.equal('msg_1')
|
||||
expect(message.contents).to.deep.equal(['new message'])
|
||||
})
|
||||
})
|
||||
|
||||
it('deduplicate messages from preloading', async function () {
|
||||
// Mock socket: we only need to emit events, not mock actual connections
|
||||
const socket = new SocketIOMock()
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({
|
||||
const { result } = renderChatContextHook({
|
||||
socket,
|
||||
})
|
||||
|
||||
fetchMock.get('express:/project/:projectId/messages', [
|
||||
{
|
||||
id: 'msg_1',
|
||||
content: 'new message',
|
||||
timestamp: Date.now(),
|
||||
user: {
|
||||
id: 'another_fake_user',
|
||||
first_name: 'another_fake_user_first_name',
|
||||
email: 'another_fake@example.com',
|
||||
fetchMock.modifyRoute('fetchMessages', {
|
||||
response: () => [
|
||||
{
|
||||
id: 'msg_1',
|
||||
content: 'new message',
|
||||
timestamp: Date.now(),
|
||||
user: {
|
||||
id: 'another_fake_user',
|
||||
first_name: 'another_fake_user_first_name',
|
||||
email: 'another_fake@example.com',
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
],
|
||||
})
|
||||
|
||||
// Mock message being received from another user
|
||||
socket.emitToClient('new-chat-message', {
|
||||
@@ -133,11 +141,13 @@ describe('ChatContext', function () {
|
||||
})
|
||||
|
||||
// Check if received the message ID
|
||||
expect(result.current.messages).to.have.length(1)
|
||||
await waitFor(() => expect(result.current.messages).to.have.length(1))
|
||||
|
||||
// Wait until initial messages have loaded
|
||||
result.current.loadInitialMessages()
|
||||
await waitForNextUpdate()
|
||||
await waitFor(
|
||||
() => expect(result.current.initialMessagesLoaded).to.be.true
|
||||
)
|
||||
|
||||
// Check if there are no message duplication
|
||||
expect(result.current.messages).to.have.length(1)
|
||||
@@ -150,29 +160,30 @@ describe('ChatContext', function () {
|
||||
it('deduplicate messages from websocket', async function () {
|
||||
// Mock socket: we only need to emit events, not mock actual connections
|
||||
const socket = new SocketIOMock()
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({
|
||||
const { result } = renderChatContextHook({
|
||||
socket,
|
||||
})
|
||||
|
||||
fetchMock.get('express:/project/:projectId/messages', [
|
||||
{
|
||||
id: 'msg_1',
|
||||
content: 'new message',
|
||||
timestamp: Date.now(),
|
||||
user: {
|
||||
id: 'another_fake_user',
|
||||
first_name: 'another_fake_user_first_name',
|
||||
email: 'another_fake@example.com',
|
||||
fetchMock.modifyRoute('fetchMessages', {
|
||||
response: [
|
||||
{
|
||||
id: 'msg_1',
|
||||
content: 'new message',
|
||||
timestamp: Date.now(),
|
||||
user: {
|
||||
id: 'another_fake_user',
|
||||
first_name: 'another_fake_user_first_name',
|
||||
email: 'another_fake@example.com',
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
],
|
||||
})
|
||||
|
||||
// Wait until initial messages have loaded
|
||||
result.current.loadInitialMessages()
|
||||
await waitForNextUpdate()
|
||||
|
||||
// Check if received the message ID
|
||||
expect(result.current.messages).to.have.length(1)
|
||||
await waitFor(() => expect(result.current.messages).to.have.length(1))
|
||||
|
||||
// Mock message being received from another user
|
||||
socket.emitToClient('new-chat-message', {
|
||||
@@ -196,13 +207,15 @@ describe('ChatContext', function () {
|
||||
|
||||
it("doesn't add received messages from the current user if a message was just sent", async function () {
|
||||
const socket = new SocketIOMock()
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({
|
||||
const { result } = renderChatContextHook({
|
||||
socket,
|
||||
})
|
||||
|
||||
// Wait until initial messages have loaded
|
||||
result.current.loadInitialMessages()
|
||||
await waitForNextUpdate()
|
||||
await waitFor(
|
||||
() => expect(result.current.initialMessagesLoaded).to.be.true
|
||||
)
|
||||
|
||||
// Send a message from the current user
|
||||
const sentMsg = 'sent message'
|
||||
@@ -219,7 +232,7 @@ describe('ChatContext', function () {
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.messages).to.have.length(1)
|
||||
await waitFor(() => expect(result.current.messages).to.have.length(1))
|
||||
|
||||
const [message] = result.current.messages
|
||||
|
||||
@@ -228,17 +241,21 @@ describe('ChatContext', function () {
|
||||
|
||||
it('adds the new message from the current user if another message was received after sending', async function () {
|
||||
const socket = new SocketIOMock()
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({
|
||||
const { result } = renderChatContextHook({
|
||||
socket,
|
||||
})
|
||||
|
||||
// Wait until initial messages have loaded
|
||||
result.current.loadInitialMessages()
|
||||
await waitForNextUpdate()
|
||||
await waitFor(
|
||||
() => expect(result.current.initialMessagesLoaded).to.be.true
|
||||
)
|
||||
|
||||
// Send a message from the current user
|
||||
const sentMsg = 'sent message from current user'
|
||||
result.current.sendMessage(sentMsg)
|
||||
act(() => {
|
||||
result.current.sendMessage(sentMsg)
|
||||
})
|
||||
|
||||
const [sentMessageFromCurrentUser] = result.current.messages
|
||||
expect(sentMessageFromCurrentUser.contents).to.deep.equal([sentMsg])
|
||||
@@ -295,24 +312,29 @@ describe('ChatContext', function () {
|
||||
})
|
||||
|
||||
it('adds messages to the list', async function () {
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({})
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.loadInitialMessages()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.messages[0].contents).to.deep.equal(['a message'])
|
||||
await waitFor(() =>
|
||||
expect(result.current.messages[0].contents).to.deep.equal(['a message'])
|
||||
)
|
||||
})
|
||||
|
||||
it("won't load messages a second time", async function () {
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({})
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.loadInitialMessages()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.initialMessagesLoaded).to.equal(true)
|
||||
await waitFor(() =>
|
||||
expect(result.current.initialMessagesLoaded).to.equal(true)
|
||||
)
|
||||
|
||||
// Calling a second time won't do anything
|
||||
result.current.loadInitialMessages()
|
||||
await waitFor(
|
||||
() => expect(result.current.initialMessagesLoaded).to.be.true
|
||||
)
|
||||
|
||||
expect(
|
||||
fetchMock.callHistory.calls('express:/project/:projectId/messages')
|
||||
).to.have.lengthOf(1)
|
||||
@@ -321,12 +343,10 @@ describe('ChatContext', function () {
|
||||
it('provides an error on failure', async function () {
|
||||
fetchMock.removeRoutes().clearHistory()
|
||||
fetchMock.get('express:/project/:projectId/messages', 500)
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({})
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.loadInitialMessages()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.error).to.exist
|
||||
await waitFor(() => expect(result.current.error).to.exist)
|
||||
expect(result.current.status).to.equal('error')
|
||||
})
|
||||
})
|
||||
@@ -343,14 +363,14 @@ describe('ChatContext', function () {
|
||||
},
|
||||
])
|
||||
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({})
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.loadMoreMessages()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.messages[0].contents).to.deep.equal([
|
||||
'first message',
|
||||
])
|
||||
await waitFor(() =>
|
||||
expect(result.current.messages[0].contents).to.deep.equal([
|
||||
'first message',
|
||||
])
|
||||
)
|
||||
|
||||
// The before query param is not set
|
||||
expect(getLastFetchMockQueryParam('before')).to.be.null
|
||||
@@ -373,20 +393,23 @@ describe('ChatContext', function () {
|
||||
},
|
||||
])
|
||||
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({})
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.loadMoreMessages()
|
||||
await waitForNextUpdate()
|
||||
await waitFor(() =>
|
||||
expect(result.current.messages[0].contents).to.have.length(50)
|
||||
)
|
||||
|
||||
// Call a second time
|
||||
result.current.loadMoreMessages()
|
||||
await waitForNextUpdate()
|
||||
|
||||
// The second request is added to the list
|
||||
// Since both messages from the same user, they are collapsed into the
|
||||
// same "message"
|
||||
expect(result.current.messages[0].contents).to.include(
|
||||
'message from second page'
|
||||
await waitFor(() =>
|
||||
expect(result.current.messages[0].contents).to.include(
|
||||
'message from second page'
|
||||
)
|
||||
)
|
||||
|
||||
// The before query param for the second request matches the timestamp
|
||||
@@ -396,23 +419,25 @@ describe('ChatContext', function () {
|
||||
})
|
||||
|
||||
it("won't load more messages if there are no more messages", async function () {
|
||||
// Mock a GET request for 49 messages. This is less the the full page size
|
||||
// (50 messages), meaning that there are no further messages to be loaded
|
||||
// Mock a GET request for 49 messages. This is less than the full page
|
||||
// size (50 messages), meaning that there are no further messages to be
|
||||
// loaded
|
||||
fetchMock.getOnce(
|
||||
'express:/project/:projectId/messages',
|
||||
createMessages(49, user)
|
||||
)
|
||||
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({})
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.loadMoreMessages()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.messages[0].contents).to.have.length(49)
|
||||
await waitFor(() =>
|
||||
expect(result.current.messages[0].contents).to.have.length(49)
|
||||
)
|
||||
|
||||
result.current.loadMoreMessages()
|
||||
|
||||
expect(result.current.atEnd).to.be.true
|
||||
await waitFor(() => expect(result.current.atEnd).to.be.true)
|
||||
|
||||
expect(
|
||||
fetchMock.callHistory.calls('express:/project/:projectId/messages')
|
||||
).to.have.lengthOf(1)
|
||||
@@ -429,7 +454,7 @@ describe('ChatContext', function () {
|
||||
)
|
||||
|
||||
const socket = new SocketIOMock()
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({
|
||||
const { result } = renderChatContextHook({
|
||||
socket,
|
||||
})
|
||||
|
||||
@@ -458,28 +483,27 @@ describe('ChatContext', function () {
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
])
|
||||
await waitForNextUpdate()
|
||||
|
||||
// Although the loaded message was resolved last, it appears first (since
|
||||
// requested messages must have come first)
|
||||
const messageContents = result.current.messages.map(
|
||||
({ contents }) => contents[0]
|
||||
)
|
||||
expect(messageContents).to.deep.equal([
|
||||
'loaded message',
|
||||
'socket message',
|
||||
])
|
||||
await waitFor(() => {
|
||||
// Although the loaded message was resolved last, it appears first (since
|
||||
// requested messages must have come first)
|
||||
const messageContents = result.current.messages.map(
|
||||
({ contents }) => contents[0]
|
||||
)
|
||||
expect(messageContents).to.deep.equal([
|
||||
'loaded message',
|
||||
'socket message',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
it('provides an error on failures', async function () {
|
||||
fetchMock.removeRoutes().clearHistory()
|
||||
fetchMock.get('express:/project/:projectId/messages', 500)
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({})
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.loadMoreMessages()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.error).to.exist
|
||||
await waitFor(() => expect(result.current.error).to.exist)
|
||||
expect(result.current.status).to.equal('error')
|
||||
})
|
||||
})
|
||||
@@ -493,14 +517,16 @@ describe('ChatContext', function () {
|
||||
.postOnce('express:/project/:projectId/messages', 200)
|
||||
})
|
||||
|
||||
it('optimistically adds the message to the list', function () {
|
||||
it('optimistically adds the message to the list', async function () {
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.sendMessage('sent message')
|
||||
|
||||
expect(result.current.messages[0].contents).to.deep.equal([
|
||||
'sent message',
|
||||
])
|
||||
await waitFor(() =>
|
||||
expect(result.current.messages[0].contents).to.deep.equal([
|
||||
'sent message',
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('POSTs the message to the backend', function () {
|
||||
@@ -534,12 +560,11 @@ describe('ChatContext', function () {
|
||||
fetchMock
|
||||
.get('express:/project/:projectId/messages', [])
|
||||
.postOnce('express:/project/:projectId/messages', 500)
|
||||
const { result, waitForNextUpdate } = renderChatContextHook({})
|
||||
const { result } = renderChatContextHook({})
|
||||
|
||||
result.current.sendMessage('sent message')
|
||||
await waitForNextUpdate()
|
||||
await waitFor(() => expect(result.current.error).to.exist)
|
||||
|
||||
expect(result.current.error).to.exist
|
||||
expect(result.current.status).to.equal('error')
|
||||
})
|
||||
})
|
||||
@@ -555,11 +580,13 @@ describe('ChatContext', function () {
|
||||
const { result } = renderChatContextHook({ socket })
|
||||
|
||||
// Receive a new message from the socket
|
||||
socket.emitToClient('new-chat-message', {
|
||||
id: 'msg_1',
|
||||
content: 'new message',
|
||||
timestamp: Date.now(),
|
||||
user,
|
||||
act(() => {
|
||||
socket.emitToClient('new-chat-message', {
|
||||
id: 'msg_1',
|
||||
content: 'new message',
|
||||
timestamp: Date.now(),
|
||||
user,
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.unreadMessageCount).to.equal(1)
|
||||
|
||||
Reference in New Issue
Block a user