diff --git a/src/modules/choose-team/ChooseTeamPage.spec.tsx b/src/modules/choose-team/ChooseTeamPage.spec.tsx index 17268382..1cf888b9 100644 --- a/src/modules/choose-team/ChooseTeamPage.spec.tsx +++ b/src/modules/choose-team/ChooseTeamPage.spec.tsx @@ -1,41 +1,112 @@ import React from "react"; -import { mount } from "enzyme"; -import { createMemoryHistory, MemoryHistory } from "history"; -import { act } from "react-dom/test-utils"; -import { findByTestId, withMockedProviders } from "../../spec_helper"; +import { withMockedProviders } from "../../spec_helper"; import { Content } from "./ChooseTeamPage"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { GET_INVITES } from "./components/InviteList"; +import { GET_TEAMS } from "./components/TeamList"; + +const mockHistoryPush = jest.fn(); + +jest.mock("react-router-dom", () => ({ + ...(jest.requireActual("react-router-dom") as any), + useHistory: () => ({ + push: mockHistoryPush, + }), +})); + +const mockWithInvites = [ + { + request: { + query: GET_INVITES, + }, + result: { + data: { + viewer: { + teamInvites: [ + { + id: '1', + team: { + id: '1', + name: 'Kabisa', + }, + }, + { + id: '2', + team: { + id: '2', + name: 'Dovetail', + }, + }, + ], + }, + }, + }, + }, + { + request: { + query: GET_TEAMS, + }, + result: { + data: { + viewer: { + memberships: [ + { + id: '1', + role: 'admin', + team: { + id: '2', + name: 'Team 1', + }, + }, + { + id: '2', + role: 'admin', + team: { + id: '2', + name: 'Team 2', + }, + }, + ], + }, + }, + }, + }, +]; describe("", () => { - let wrapper: any; - let history: MemoryHistory; + it("renders the invite list", async () => { + render(withMockedProviders(, mockWithInvites)); - beforeEach(async () => { - history = createMemoryHistory(); + const inviteList = screen.getByTestId("invite-list"); - await act(async () => { - wrapper = mount(withMockedProviders()); - }); + expect(inviteList).toBeInTheDocument(); }); - it("renders the invite list", () => { - expect(findByTestId(wrapper, "invite-list").length).toBe(1); - }); + it("renders the team list", async () => { + render(withMockedProviders(, mockWithInvites)); - it("renders the team list", () => { - expect(wrapper.find("TeamList").length).toBe(1); + const teamList = screen.getByTestId("personal-team-list"); + + expect(teamList).toBeInTheDocument(); }); it("renders the create team button", () => { - expect(wrapper.find(".button").length).toBe(1); + render(withMockedProviders(, mockWithInvites)); + + const createTeamButton = screen.getByRole("button", { + name: "Create team", + }); + expect(createTeamButton).toBeInTheDocument(); }); it("navigates to the create team page", async () => { - await act(async () => { - findByTestId(wrapper, "create-team").hostNodes().simulate("click"); + render(withMockedProviders(, mockWithInvites)); - await wrapper.update(); - - expect(history.location.pathname).toBe("/create-team"); + const createTeamButton = screen.getByRole("button", { + name: "Create team", }); + fireEvent.click(createTeamButton); + + expect(mockHistoryPush).toHaveBeenCalledWith("/create-team"); }); }); diff --git a/src/modules/choose-team/ChooseTeamPage.tsx b/src/modules/choose-team/ChooseTeamPage.tsx index 7c18ef9b..c1e0a269 100644 --- a/src/modules/choose-team/ChooseTeamPage.tsx +++ b/src/modules/choose-team/ChooseTeamPage.tsx @@ -15,13 +15,12 @@ export function Content(): React.ReactElement { return (

Your invites

- +

Your teams

- + Or )).toBe(true); + render( + withMockedProviders(, mocks) + ); + + expect(screen.getByRole("button", { name: "Create team" })).toBeInTheDocument(); }); it('handles input correctly', async () => { - const component: any = wrapper.find('CreateTeamPage').instance(); - - await act(async () => { - expect(component.state.name).toBe(''); + render( + withMockedProviders(, mocks) + ); + const input = screen.getByPlaceholderText("Team name"); + expect(input).toHaveValue(""); - simulateInputChange(wrapper, 'name-input', 'name', 'Kabisa'); - await wrapper.update(); + fireEvent.change(input, { target: { value: "Kabisa" }}); - expect(component.state.name).toBe('Kabisa'); - }); + expect(input).toHaveValue("Kabisa"); }); it('returns an error if the name is null', async () => { - await act(async () => { - findByTestId(wrapper, 'create-team-button').hostNodes().simulate('click'); + render( + withMockedProviders(, mocks) + ); + const button = screen.getByRole("button", { name: "Create team" }); - await wait(0); - wrapper.update(); + userEvent.click(button); - expect(findByTestId(wrapper, 'error-message').text()).toBe('Name can\'t be blank.'); - }); + expect(screen.queryByText("Name can't be blank.")); }); it('Sets the loading state', async () => { - const component = wrapper.find('CreateTeamPage').instance(); - await act(async () => { - component.setState({ name: 'Kabisa' }); - await wrapper.update(); + const { container } = render( + withMockedProviders(, mocks) + ); + const input = screen.getByPlaceholderText("Team name"); + const button = screen.getByRole("button", { name: "Create team" }); - findByTestId(wrapper, 'create-team-button').hostNodes().simulate('click'); + fireEvent.change(input, { target: { value: "Kabisa" }}); + userEvent.click(button); - expect(wrapper.find('.loading').length).toBe(1); - }); + await waitFor(() => expect(container.getElementsByClassName("loading").length).toBe(1)); }); it('shows when there is an error', async () => { - wrapper = mount(withMockedProviders(, mocksWithError)); - const component = wrapper.find('CreateTeamPage').instance(); - - await act(async () => { - component.setState({ name: 'Kabisa' }); - await wrapper.update(); + render( + withMockedProviders(, mocksWithError) + ); + const button = screen.getByRole("button", { name: "Create team" }); - findByTestId(wrapper, 'create-team-button').hostNodes().simulate('click'); + userEvent.click(button); - await wait(0); - await wrapper.update(); - - expect(findByTestId(wrapper, 'error-message').text()).toBe('It broke'); - }); + expect(screen.queryByText("It broke")); }); it('calls the mutation if the name is not null', async () => { - const component = wrapper.find('CreateTeamPage').instance(); - await act(async () => { - component.setState({ name: 'Kabisa' }); - await wrapper.update(); - - findByTestId(wrapper, 'create-team-button').hostNodes().simulate('click'); - - await wait(0); + render( + withMockedProviders(, mocks) + ); + const input = screen.getByPlaceholderText("Team name"); + const button = screen.getByRole("button", { name: "Create team" }); - wrapper.update(); + fireEvent.change(input, { target: { value: "Kabisa" }}); + userEvent.click(button); - expect(mutationCalled).toBe(true); - }); + await waitFor(() => expect(mutationCalled).toBe(true)); }); it('sets the team id in local storage if the mutation is successful', async () => { - const component = wrapper.find('CreateTeamPage').instance(); - - await act(async () => { - component.setState({ name: 'Kabisa' }); - await wrapper.update(); - - findByTestId(wrapper, 'create-team-button').hostNodes().simulate('click'); - - await wait(0); + render( + withMockedProviders(, mocks) + ); + const input = screen.getByPlaceholderText("Team name"); + const button = screen.getByRole("button", { name: "Create team" }); - wrapper.update(); + fireEvent.change(input, { target: { value: "Kabisa" }}); + userEvent.click(button); - expect(Storage.setItem).toBeCalledWith('team_id', '1'); - }); + await waitFor(() => expect(Storage.setItem).toBeCalledWith('team_id', '1')); }); }); diff --git a/src/modules/choose-team/CreateTeamPage.tsx b/src/modules/choose-team/CreateTeamPage.tsx index 89ddd365..903d1046 100644 --- a/src/modules/choose-team/CreateTeamPage.tsx +++ b/src/modules/choose-team/CreateTeamPage.tsx @@ -1,9 +1,8 @@ -import React, { ChangeEvent, Component } from "react"; +import React, { useState } from "react"; import { Button, Form, Message, Segment } from "semantic-ui-react"; -import { Mutation } from "@apollo/client/react/components"; -import { gql } from "@apollo/client"; +import { gql, useMutation } from "@apollo/client"; import { toast } from "react-toastify"; -import { withRouter } from "react-router-dom"; +import { useHistory } from "react-router-dom"; import settings from "../../config/settings"; import { ERROR_NAME_BLANK, getGraphqlError } from "../../support"; import { Navigation } from "../../components/navigation"; @@ -11,7 +10,6 @@ import { PATH_FEED } from "../../routes"; import { Storage } from "../../support/storage"; import s from "./CreateTeamPage.module.scss"; import { FormWrapper } from "../../components"; -import { History } from "history"; export const MUTATION_CREATE_TEAM = gql` mutation CreateTeam($name: String!) { @@ -35,120 +33,83 @@ export interface CreateTeamResult { }; } -export interface Props { - history: History; -} - -export interface State { - name: string; - error: string; -} - -class CreateTeamPage extends Component { - initialState: State; +const CreateTeamPage = () => { + const history = useHistory(); - constructor(props: Props) { - super(props); - - this.state = { - name: "", - error: "", - }; - - this.initialState = this.state; - - this.createTeam = this.createTeam.bind(this); - this.handleChange = this.handleChange.bind(this); - this.hasErrors = this.hasErrors.bind(this); - } + const [name, setName] = useState(''); + const [error, setError] = useState(''); - hasErrors() { - const { name } = this.state; - this.setState({ error: "" }); + const [createTeam, { loading }] = useMutation(MUTATION_CREATE_TEAM, { + onError: (error) => setError(getGraphqlError(error)), + onCompleted: ({ createTeam }) => { + Storage.setItem(settings.TEAM_ID_TOKEN, createTeam.team.id); + toast.info('Team created successfully!'); + history.push(PATH_FEED); + }, + }); + const hasErrors = () => { + setError(''); if (!name) { - this.setState({ error: ERROR_NAME_BLANK }); + setError(ERROR_NAME_BLANK); return true; } return false; - } - - createTeam(mutate: any) { - const { name } = this.state; + }; - if (this.hasErrors()) { + const handleCreateTeam = () => { + if (hasErrors()) { return; } - - mutate({ + createTeam({ variables: { name, }, }); - } - - handleChange(e: ChangeEvent, { name, value }: any) { - // @ts-ignore - this.setState({ [name]: value }); - } + }; - render() { - const content = ( -
- - mutation={MUTATION_CREATE_TEAM} - onError={(error) => this.setState({ error: getGraphqlError(error) })} - onCompleted={({ createTeam }) => { - this.setState(this.initialState); - Storage.setItem(settings.TEAM_ID_TOKEN, createTeam.team.id); - toast.info("Team created successfully!"); - this.props.history.push(PATH_FEED); - }} - > - {(createTeam, { error, loading }) => ( -
- - - {this.state.error && ( - - Unable to create team -

{this.state.error}

-
- )} - - )} - -
- ); + const content = ( +
+
+ setName(e.target.value)} + /> + + {error && ( + + Unable to create team +

{error}

+
+ )} + +
+ ); + + return ( +
+ + {content} + + +
+ ); +}; + +export default CreateTeamPage; - return ( -
- - {content} - - -
- ); - } -} -// @ts-ignore -export default withRouter(CreateTeamPage); diff --git a/src/modules/choose-team/components/InviteList.tsx b/src/modules/choose-team/components/InviteList.tsx index 82bf9e4b..843bb511 100644 --- a/src/modules/choose-team/components/InviteList.tsx +++ b/src/modules/choose-team/components/InviteList.tsx @@ -24,33 +24,35 @@ export interface InvitesResult { } const InviteList = () => ( - - query={GET_INVITES} - pollInterval={2000} - fetchPolicy="network-only" - > - {({ loading, error, data }) => { - if (loading) return

Loading...

; - if (error) - return ( -

- {error.message} -

- ); +
+ + query={GET_INVITES} + pollInterval={2000} + fetchPolicy="network-only" + > + {({ loading, error, data }) => { + if (loading) return

Loading...

; + if (error) + return ( +

+ {error.message} +

+ ); - if (!data || !data.viewer.teamInvites.length) { - return

No invites.

; - } + if (!data || !data.viewer.teamInvites.length) { + return

No invites.

; + } - return ( -
- {data.viewer.teamInvites.map((invite) => ( - - ))} -
- ); - }} - + return ( +
+ {data.viewer.teamInvites.map((invite) => ( + + ))} +
+ ); + }} + +
); export default InviteList; diff --git a/src/modules/choose-team/components/TeamList.tsx b/src/modules/choose-team/components/TeamList.tsx index 855d064f..46e934ea 100644 --- a/src/modules/choose-team/components/TeamList.tsx +++ b/src/modules/choose-team/components/TeamList.tsx @@ -42,42 +42,44 @@ function TeamList(): React.ReactElement { const history = useHistory(); return ( - - query={GET_TEAMS} - pollInterval={2000} - fetchPolicy="network-only" - > - {({ loading, error, data }) => { - if (loading) return

Loading...

; - if (error) return

{error.message}

; +
+ + query={GET_TEAMS} + pollInterval={2000} + fetchPolicy="network-only" + > + {({ loading, error, data }) => { + if (loading) return

Loading...

; + if (error) return

{error.message}

; - if (!data || !data.viewer.memberships.length) { - return

No teams.

; - } - const { memberships } = data.viewer; + if (!data || !data.viewer.memberships.length) { + return

No teams.

; + } + const { memberships } = data.viewer; - if (memberships.length === 1) { - if (!Storage.getItem(settings.TEAM_ID_TOKEN)) { - selectTeam(memberships[0].team.id, memberships[0].role); - history.push(PATH_FEED); + if (memberships.length === 1) { + if (!Storage.getItem(settings.TEAM_ID_TOKEN)) { + selectTeam(memberships[0].team.id, memberships[0].role); + history.push(PATH_FEED); + } } - } - return ( - - {memberships.map((membership) => ( - - ))} - - ); - }} - + return ( + + {memberships.map((membership) => ( + + ))} + + ); + }} + +
); }