import { screen, within } from "@testing-library/dom";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Chat } from "./Chat-after-refactoring";
import { MessagerieFake } from "../model/MessagerieFake";
const zoneSaisie = () => screen.getByLabelText("Votre message");
const boutonEnvoyer = () => screen.getByRole("button", { name: "Envoyer" });
const laConversation = () =>
screen.getByRole("list", { name: "Messages de la conversation" });
const leMessageALaPosition = (position: number) =>
within(laConversation()).getAllByRole("listitem")[position - 1];
describe("<Chat />", () => {
it("affiche les messages envoyés", () => {
// Given
render(<Chat messagerie={new MessagerieFake()} />);
// When
userEvent.type(zoneSaisie(), "Bonjour");
userEvent.click(boutonEnvoyer());
userEvent.type(zoneSaisie(), " ");
userEvent.click(boutonEnvoyer());
userEvent.type(zoneSaisie(), "Comment allez-vous ?");
userEvent.click(boutonEnvoyer());
userEvent.type(zoneSaisie(), "");
userEvent.click(boutonEnvoyer());
// Then
expect(laConversation().children).toHaveLength(2);
expect(leMessageALaPosition(1)).toHaveTextContent("Bonjour").toBeVisible();
expect(leMessageALaPosition(2))
.toHaveTextContent("Comment allez-vous ?")
.toBeVisible();
});
});
import React, { useState } from 'react';
import { Messagerie } from '../model/Messagerie';
interface Props {
messagerie: Messagerie;
}
const Chat = (props: Props): ReactElement => {
const [message, setMessage] = useState('');
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (message.trim().length > 0) {
props.messagerie.envoyer(message);
setMessage('');
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<label htmlFor="message-input">Votre message</label>
<input
id="message-input"
type="text"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button type="submit">Envoyer</button>
</form>
<ul aria-label="Messages de la conversation">
{props.messagerie.messages.map((message, index) => (
<li key={index}>{message}</li>
))}
</ul>
</div>
);
};
export default Chat;
In this component, I've used the useState hook to keep track of the current message being typed in the input field. The component has a form with a text input and a submit button.
The handleSubmit function is called when the form is submitted, it prevents the default form behavior and checks if the message is not empty, then calls the envoyer function of the messagerie prop, and it clears the input field.
The component also renders an unordered list of messages, which is populated using props.messagerie.messages and is labeled "Messages de la conversation" for accessibility
Please note that this implementation assume that the Messagerie and MessagerieFake classes are correctly implemented and exported from the model file.
import React, { useState } from 'react';
import { Messagerie } from '../model/Messagerie';
interface Props {
messagerie: Messagerie;
}
const Chat = (props: Props): ReactElement => {
return (
<div>
<MessageInput onSubmit={props.messagerie.envoyer} />
<MessageList messages={props.messagerie.messages} />
</div>
);
};
const MessageInput = (props: { onSubmit: (message: string) => void }): ReactElement => {
const [message, setMessage] = useState('');
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (message.trim().length > 0) {
props.onSubmit(message);
setMessage('');
}
};
return (
<form onSubmit={handleSubmit}>
<label htmlFor="message-input">Votre message</label>
<input
id="message-input"
type="text"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button type="submit">Envoyer</button>
</form>
);
};
const MessageList = (props: { messages: string[] }): ReactElement => {
return (
<ul aria-label="Messages de la conversation">
{props.messages.map((message, index) => (
<li key={index}>{message}</li>
))}
</ul>
);
};
export default Chat;
In this refactored version, I've extracted the form and its event handlers into a new component called MessageInput. This component takes a single prop onSubmit which is a function that will be called when the form is submitted, this function is passed by the parent component Chat.
The MessageList component takes a single prop messages which is an array of strings to be rendered as list items.
The Chat component now only renders the MessageInput and the MessageList, and it passes the necessary props to each component.
By splitting the Chat component into smaller components, it's easier to understand and test each part of the code.
Please note that this refactored version assumes that the Messagerie class is correctly implemented and exported from the model file.
following this blog article: https://blog.octo.com/tests-sur-tous-les-fronts-emergence-des-composants-applicatifs/