I am testing a component's submit button and the test passes but I would really like this ECONNREFUSED
error to go away and understand why this is happening. The line of code that throws the error in my test is await userEvent.click(submitBtn)
. If I comment out that line of code, the error goes away.
I read this StackOverflow and it seems to be a similar issue. The answer states:
EConnectionRefused means there is still an API call that is being executed. I don't know how you spy on yours. The reason it's not there when you comment out your
waitFor
is that your test exits before you execute it. You probably do get an act warning in that case if you set state after the call.
I am not familiar with spying on API calls and I do not get any act
warnings. My guess is it has to do with this test being async, but I tried testing without it. So, instead of using await
in the line that was throwing an error I used the waitFor
method from React-Testing-Library. The issue I had with this is that the button never gets clicked.
The specific line of code I used with the waitFor
method was waitFor(() => userEvent.click(submitBtn))
.
Another cause of the problem may be how I am calling my render function with the submit jest function with these two lines of code in my test:
const submit = jest.fn()
renderWritingField(userData, unirepData, postData, submit, type, page)
Here is the test:
// import 'whatwg-fetch'
import { screen, render, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import UnirepContext from '../../src/context/Unirep'
import UserContext from '../../src/context/User'
import PostContext from '../../src/context/Post'
import WritingField from '../components/writingField/writingField'
// abstracted render function
function renderWritingField(
userData,
unirepData,
postData,
type,
page,
submit
) {
return render(
<UserContext.Provider value={userData}>
<UnirepContext.Provider value={unirepData}>
<PostContext.Provider value={postData}>
<WritingField
type={type}
submit={submit}
submitBtnName={'subbtn'}
onClick={jest.fn()}
/>
</PostContext.Provider>
</UnirepContext.Provider>
</UserContext.Provider>
)
}
test('should throw error text if user does not enter any value for title or content and clicks submit button', async () => {
const page = '/user'
const type = 0
const unirepData = {
unirepConfig: {
commentReptation: 30,
},
}
const postData = {
commentsById: {
commentId: {
id: 'commentId',
content: 'string',
post_time: '00',
reputation: 30,
epoch_key: 'epoch_key test',
},
},
setDraft: jest.fn(),
postDraft: {
title: 'Post Draft title',
content: 'Post Draft content',
},
commentDraft: {
title: 'Comment Draft title',
content: 'Comment Draft content',
},
}
const userData = {
userState: true,
currentEpochKeys: ['user epoch_key test'],
}
const submit = jest.fn()
renderWritingField(userData, unirepData, postData, submit, type, page)
const submitBtn = screen.getByText(/subbtn/i)
expect(submitBtn).toBeInTheDocument()
await userEvent.click(submitBtn)
expect(
screen.getByText(/please input either title or content./i)
).toBeInTheDocument()
})
Component that is being tested:
import { useState, useContext, useEffect } from 'react'
import 'react-circular-progressbar/dist/styles.css'
import { observer } from 'mobx-react-lite'
import UnirepContext from '../../context/Unirep'
import UserContext from '../../context/User'
import PostContext from '../../context/Post'
import HelpWidget from '../helpWidget/helpWidget'
import { DataType, InfoType } from '../../constants'
import { shortenEpochKey } from '../../utils'
type Props = {
type: DataType
submit: (
title: string,
content: string,
epkNonce: number,
reputation: number
) => void
submitBtnName: string
onClick: (event: any) => void
}
const WritingField = (props: Props) => {
const unirepConfig = useContext(UnirepContext)
const user = useContext(UserContext)
const postContext = useContext(PostContext)
const [title, setTitle] = useState<string>('')
const [content, setContent] = useState<string>('')
const [epkNonce, setEpkNonce] = useState<number>(0)
const [errorMsg, setErrorMsg] = useState<string>('')
const defaultRep =
props.type === DataType.Post
? unirepConfig.postReputation
: unirepConfig.commentReputation
const [reputation, setReputation] = useState(defaultRep)
useEffect(() => {
if (props.type === DataType.Post && postContext.postDraft) {
setTitle(postContext.postDraft.title)
setContent(postContext.postDraft.content)
} else if (
props.type === DataType.Comment &&
postContext.commentDraft
) {
setContent(postContext.commentDraft.content)
}
}, [])
useEffect(() => {
setErrorMsg('')
}, [title, content, reputation, epkNonce])
const onClickField = (event: any) => {
props.onClick(event)
}
const handleTitleInput = (event: any) => {
setTitle(event.target.value)
postContext.setDraft(props.type, event.target.value, content)
}
const handleContentInput = (event: any) => {
setContent(event.target.value)
postContext.setDraft(props.type, title, event.target.value)
}
const handleRepInput = (event: any) => {
setReputation(+event.target.value)
}
const submit = () => {
if (!user.userState) {
setErrorMsg('Please sign up or sign in')
} else {
if (title.length === 0 && content.length === 0) {
setErrorMsg('Please input either title or content.')
} else {
props.submit(title, content, epkNonce, reputation)
}
}
}
return (
<div className="writing-field" onClick={onClickField}>
{props.type === DataType.Post ? (
<input
type="text"
placeholder="Give an eye-catching title"
onChange={handleTitleInput}
value={title}
/>
) : (
<div></div>
)}
{props.type === DataType.Post ? (
<textarea onChange={handleContentInput} value={content} />
) : (
<textarea
autoFocus
onChange={handleContentInput}
value={content}
/>
)}
<div className="info-row">
<div className="element">
<div className="name">
Post as <HelpWidget type={InfoType.epk4Post} />
</div>
<div className="epks">
{!user.userState ? (
<div>somethings wrong...</div>
) : (
user.currentEpochKeys.map((epk, i) => (
<div
className={
i === epkNonce ? 'epk chosen' : 'epk'
}
onClick={() => setEpkNonce(i)}
key={epk}
>
{shortenEpochKey(epk)}
</div>
))
)}
</div>
</div>
<div className="element">
<div className="name">
My Rep display <HelpWidget type={InfoType.rep} />
</div>
<div className="rep-chooser">
<input
type="range"
min={defaultRep}
max={
user.userState ? user.netReputation : defaultRep
}
onChange={handleRepInput}
value={reputation}
/>
<input
type="text"
value={reputation}
onChange={handleRepInput}
/>
</div>
</div>
</div>
<div className="submit-btn" onClick={submit}>
{props.submitBtnName}
</div>
{errorMsg.length > 0 ? (
<div className="error">{errorMsg}</div>
) : (
<div></div>
)}
</div>
)
}
export default observer(WritingField)
The error in the console:
Error: Error: connect ECONNREFUSED 127.0.0.1:3001
at Object.dispatchError (/Users/anthonymadia/code/work/Unirep-Social/packages/frontend/node_modules/jsdom/lib/jsdom/living/xhr/xhr-utils.js:63:19)
at Request.<anonymous> (/Users/anthonymadia/code/work/Unirep-Social/packages/frontend/node_modules/jsdom/lib/jsdom/living/xhr/XMLHttpRequest-impl.js:655:18)
at Request.emit (node:events:402:35)
at ClientRequest.<anonymous> (/Users/anthonymadia/code/work/Unirep-Social/packages/frontend/node_modules/jsdom/lib/jsdom/living/helpers/http-request.js:121:14)
at ClientRequest.emit (node:events:390:28)
at Socket.socketErrorListener (node:_http_client:447:9)
at Socket.emit (node:events:390:28)
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
type: 'XMLHttpRequest'
}