I want to test if the messageListener appends the data to the messages array state. However I've gotten some blocks on this. 1. Can't test methods inside a stateless component 2. React hooks cannot be used outside a component function.
I can't figure out how to get around this? Should I use a different approach for testing this? Should I extract it into a custom hook? I'm new to both Enzyme and React hooks. I basically want to test if given that socket.io receives the MESSAGE event, the component will append another item to the message array to be displayed inside MessageFeed.
The full project can also be seen on https://github.com/Hyllesen/react-chat
App.js
import React from "react";
import UsernameInput from "components/UsernameInput";
import MessageFeed from "components/MessageFeed";
import io from "socket.io-client";
import * as eventTypes from "eventTypes";
function joinWithUsername(username) {
const socket = io("http://localhost:3001");
socket.emit(eventTypes.USER_JOIN, { username });
socket.on(eventTypes.MESSAGE, messageListener);
}
function messageListener(data, setMessages) {
setMessages([...messages, data]);
}
const App = () => {
const [messages, setMessages] = React.useState([]);
return (
<div className="App">
<UsernameInput onSubmit={joinWithUsername} />
<MessageFeed messages={messages} />
</div>
);
};
export default App;
export { joinWithUsername, messageListener };
App.test.js
import React from "react";
import App, { joinWithUsername, messageListener } from "../App";
import { shallow } from "enzyme";
import io from "socket.io-client";
import * as eventTypes from "eventTypes";
let wrapper, setState, useStateSpy;
jest.mock("socket.io-client", () => {
const emit = jest.fn();
const on = jest.fn();
const socket = { emit, on };
return jest.fn(() => socket);
});
describe("App", () => {
beforeEach(() => {
wrapper = shallow(<App />);
setState = jest.fn();
useStateSpy = jest.spyOn(React, "useState");
useStateSpy.mockImplementation(init => [init, setState]);
});
afterEach(() => jest.clearAllMocks());
it("has a usernameinput component", () => {
expect(wrapper.find("UsernameInput").length).toBe(1);
});
it("has a joinWithUsername function", () => {
const usernameInput = wrapper.find("UsernameInput").props();
console.log(usernameInput.onSubmit);
expect(usernameInput.onSubmit).toBe(joinWithUsername);
});
it("connect to socket.io server", () => {
joinWithUsername("Bobby");
expect(io).toHaveBeenCalledWith("http://localhost:3001");
});
it("emits the USER_JOIN event", () => {
joinWithUsername("John");
const socket = io();
expect(socket.emit).toHaveBeenCalledWith(eventTypes.USER_JOIN, {
username: "John"
});
});
it("listens for the MESSAGE event", () => {
joinWithUsername("John");
const socket = io();
expect(socket.on).toHaveBeenCalledWith(eventTypes.MESSAGE, messageListener);
});
it("Appends a message to state when message is received", () => {
const testData = {
from: "John",
message: "Hello everyone!"
};
messageListener(testData);
expect(setState).toHaveBeenCalledWith(testData); //ReferenceError: setMessages is not defined
});
});