1

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'
    }
AAMCODE
  • 415
  • 1
  • 7
  • 20

0 Answers0