Skip to content

Commit 844206e

Browse files
authored
Merge pull request #207 from amattu2/amattu2/issue206
fix: Post Chip embeds missing interactivity
2 parents 384314d + 1870410 commit 844206e

File tree

14 files changed

+434
-35
lines changed

14 files changed

+434
-35
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import React from "react";
2+
import { render } from "@testing-library/react";
3+
import { BrowserRouter as Router } from "react-router-dom";
4+
import userEvent from "@testing-library/user-event";
5+
import ListLinkChip from "./index";
6+
import { LookupStatus, ListLookupResponse } from "../../hooks/useListLookup";
7+
8+
const mockUseListLookup = jest.fn<ListLookupResponse, [string]>();
9+
jest.mock("../../hooks/useListLookup", () => ({
10+
...jest.requireActual("../../hooks/useListLookup"),
11+
__esModule: true,
12+
default: (uuid: string) => mockUseListLookup(uuid),
13+
}));
14+
15+
describe("Basic Functionality", () => {
16+
beforeEach(() => {
17+
jest.clearAllMocks();
18+
});
19+
20+
it("should render a chip embed for the list if it exists", () => {
21+
mockUseListLookup.mockReturnValue([
22+
{
23+
status: LookupStatus.Success,
24+
list: { uuid: "test-uuid", name: "Test List" } as List,
25+
},
26+
jest.fn(),
27+
jest.fn(),
28+
]);
29+
30+
const { getByTestId } = render(<ListLinkChip uuid="test-uuid" />, { wrapper: Router });
31+
32+
expect(getByTestId("list-chip")).toBeInTheDocument();
33+
expect(getByTestId("list-chip")).toHaveTextContent("Test List");
34+
expect(getByTestId("list-chip")).toHaveAttribute("href", "/list/test-uuid");
35+
expect(getByTestId("list-chip")).toHaveStyle("cursor: pointer;");
36+
});
37+
38+
it("should open the list page when the chip is clicked", async () => {
39+
mockUseListLookup.mockReturnValue([
40+
{
41+
status: LookupStatus.Success,
42+
list: { uuid: "test-uuid", name: "Test List" } as List,
43+
},
44+
jest.fn(),
45+
jest.fn(),
46+
]);
47+
48+
const { getByTestId } = render(<ListLinkChip uuid="test-uuid" />, { wrapper: Router });
49+
50+
await userEvent.click(getByTestId("list-chip"));
51+
52+
expect(window.location.pathname).toBe("/list/test-uuid");
53+
});
54+
55+
it("should render the original link to the list page if the list does not have a name", () => {
56+
mockUseListLookup.mockReturnValue([
57+
{
58+
status: LookupStatus.Success,
59+
list: { uuid: "test-uuid" } as List,
60+
},
61+
jest.fn(),
62+
jest.fn(),
63+
]);
64+
65+
const { getByText, queryByTestId } = render(<ListLinkChip uuid="test-uuid" />, {
66+
wrapper: Router,
67+
});
68+
69+
expect(getByText(`${window.origin}/list/test-uuid`)).toBeInTheDocument();
70+
expect(queryByTestId("list-chip")).not.toBeInTheDocument();
71+
});
72+
73+
it("should render a link to the list page if the list does not exist", () => {
74+
mockUseListLookup.mockReturnValue([
75+
{
76+
status: LookupStatus.Success,
77+
list: null,
78+
},
79+
jest.fn(),
80+
jest.fn(),
81+
]);
82+
83+
const { getByText, queryByTestId } = render(<ListLinkChip uuid="test-uuid" />, {
84+
wrapper: Router,
85+
});
86+
87+
expect(getByText(`${window.origin}/list/test-uuid`)).toBeInTheDocument();
88+
expect(queryByTestId("list-chip")).not.toBeInTheDocument();
89+
});
90+
91+
it("should render a link to the list page if the list lookup fails", () => {
92+
mockUseListLookup.mockReturnValue([
93+
{
94+
status: LookupStatus.Error,
95+
list: null,
96+
},
97+
jest.fn(),
98+
jest.fn(),
99+
]);
100+
101+
const { getByText, queryByTestId } = render(<ListLinkChip uuid="test-uuid" />, {
102+
wrapper: Router,
103+
});
104+
105+
expect(getByText(`${window.origin}/list/test-uuid`)).toBeInTheDocument();
106+
expect(queryByTestId("list-chip")).not.toBeInTheDocument();
107+
});
108+
109+
it("should render a link to the list page if the list lookup is pending", () => {
110+
mockUseListLookup.mockReturnValue([
111+
{
112+
status: LookupStatus.Loading,
113+
list: null,
114+
},
115+
jest.fn(),
116+
jest.fn(),
117+
]);
118+
119+
const { getByText, queryByTestId } = render(<ListLinkChip uuid="test-uuid" />, {
120+
wrapper: Router,
121+
});
122+
123+
expect(getByText(`${window.origin}/list/test-uuid`)).toBeInTheDocument();
124+
expect(queryByTestId("list-chip")).not.toBeInTheDocument();
125+
});
126+
});

src/components/ListLinkChip/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const ListLinkChip: FC<Props> = ({ uuid }: Props) => {
3030
to={`/list/${uuid}`}
3131
size="small"
3232
data-testid="list-chip"
33+
clickable
3334
/>
3435
);
3536
};
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React from "react";
2+
import { render } from "@testing-library/react";
3+
import { BrowserRouter as Router } from "react-router-dom";
4+
import userEvent from "@testing-library/user-event";
5+
import MentionChip from "./index";
6+
import { LookupStatus, UUIDLookupResponse } from "../../hooks/useUUIDLookup";
7+
8+
const mockUseUUIDLookup = jest.fn<UUIDLookupResponse, [string]>();
9+
jest.mock("../../hooks/useUUIDLookup", () => ({
10+
...jest.requireActual("../../hooks/useUUIDLookup"),
11+
__esModule: true,
12+
default: (handle: string) => mockUseUUIDLookup(handle),
13+
}));
14+
15+
describe("Basic Functionality", () => {
16+
beforeEach(() => {
17+
jest.clearAllMocks();
18+
});
19+
20+
it("should render a chip embed for the profile if it exists", () => {
21+
mockUseUUIDLookup.mockReturnValue([LookupStatus.Success, { uuid: "test-user-uuid" }]);
22+
23+
const { getByTestId } = render(<MentionChip handle="test-user-handle" />, { wrapper: Router });
24+
25+
expect(getByTestId("mention-chip")).toBeInTheDocument();
26+
expect(getByTestId("mention-chip")).toHaveTextContent("test-user-handle");
27+
expect(getByTestId("mention-chip")).toHaveAttribute("href", "/profile/test-user-uuid");
28+
expect(getByTestId("mention-chip")).toHaveStyle("cursor: pointer;");
29+
});
30+
31+
it("should open the profile page when the chip is clicked", async () => {
32+
mockUseUUIDLookup.mockReturnValue([LookupStatus.Success, { uuid: "test-user-uuid" }]);
33+
34+
const { getByTestId } = render(<MentionChip handle="test-user-handle" />, { wrapper: Router });
35+
36+
await userEvent.click(getByTestId("mention-chip"));
37+
38+
expect(window.location.pathname).toBe("/profile/test-user-uuid");
39+
});
40+
41+
it("should render the original handle if the lookup returns no uuid", () => {
42+
mockUseUUIDLookup.mockReturnValue([LookupStatus.Success, { uuid: null }]);
43+
44+
const { getByText, queryByTestId } = render(<MentionChip handle="test-user-handle" />, {
45+
wrapper: Router,
46+
});
47+
48+
expect(getByText("@test-user-handle")).toBeInTheDocument();
49+
expect(queryByTestId("mention-chip")).not.toBeInTheDocument();
50+
});
51+
52+
it("should render a link to the list page if the list lookup fails", () => {
53+
mockUseUUIDLookup.mockReturnValue([LookupStatus.Error, { uuid: null }]);
54+
55+
const { getByText, queryByTestId } = render(<MentionChip handle="test-user-handle" />, {
56+
wrapper: Router,
57+
});
58+
59+
expect(getByText("@test-user-handle")).toBeInTheDocument();
60+
expect(queryByTestId("mention-chip")).not.toBeInTheDocument();
61+
});
62+
63+
it("should render a link to the list page if the list lookup is pending", () => {
64+
mockUseUUIDLookup.mockReturnValue([LookupStatus.Loading, { uuid: null }]);
65+
66+
const { getByText, queryByTestId } = render(<MentionChip handle="test-user-handle" />, {
67+
wrapper: Router,
68+
});
69+
70+
expect(getByText("@test-user-handle")).toBeInTheDocument();
71+
expect(queryByTestId("mention-chip")).not.toBeInTheDocument();
72+
});
73+
});

src/components/MentionChip/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const MentionChip: FC<Props> = ({ handle }: Props) => {
2929
to={`/profile/${uuid}`}
3030
size="small"
3131
data-testid="mention-chip"
32+
clickable
3233
/>
3334
);
3435
};
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React from "react";
2+
import { render } from "@testing-library/react";
3+
import { BrowserRouter as Router } from "react-router-dom";
4+
import userEvent from "@testing-library/user-event";
5+
import ProfileLinkChip from "./index";
6+
import { LookupStatus, ProfileLookupResponse } from "../../hooks/useProfileLookup";
7+
8+
const mockUseProfileLookup = jest.fn<ProfileLookupResponse, [string]>();
9+
jest.mock("../../hooks/useProfileLookup", () => ({
10+
...jest.requireActual("../../hooks/useProfileLookup"),
11+
__esModule: true,
12+
default: (handle: string) => mockUseProfileLookup(handle),
13+
}));
14+
15+
describe("Basic Functionality", () => {
16+
beforeEach(() => {
17+
jest.clearAllMocks();
18+
});
19+
20+
it("should render a chip embed for the username if the profile exists", () => {
21+
mockUseProfileLookup.mockReturnValue([
22+
{ status: LookupStatus.Success, profile: { username: "test_user" } as Profile },
23+
jest.fn(),
24+
jest.fn(),
25+
]);
26+
27+
const { getByTestId } = render(<ProfileLinkChip uuid="test-uuid" />, {
28+
wrapper: Router,
29+
});
30+
31+
expect(getByTestId("mention-chip")).toBeInTheDocument();
32+
expect(getByTestId("mention-chip")).toHaveTextContent("test_user");
33+
expect(getByTestId("mention-chip")).toHaveAttribute("href", "/profile/test-uuid");
34+
expect(getByTestId("mention-chip")).toHaveStyle("cursor: pointer;");
35+
});
36+
37+
it("should open the profile page when the chip is clicked", async () => {
38+
mockUseProfileLookup.mockReturnValue([
39+
{ status: LookupStatus.Success, profile: { username: "test_user" } as Profile },
40+
jest.fn(),
41+
jest.fn(),
42+
]);
43+
44+
const { getByTestId } = render(<ProfileLinkChip uuid="test-user-uuid" />, {
45+
wrapper: Router,
46+
});
47+
48+
await userEvent.click(getByTestId("mention-chip"));
49+
50+
expect(window.location.pathname).toBe("/profile/test-user-uuid");
51+
});
52+
53+
it("should render the localized profile link if the lookup returns no profile", () => {
54+
mockUseProfileLookup.mockReturnValue([
55+
{ status: LookupStatus.Success, profile: null },
56+
jest.fn(),
57+
jest.fn(),
58+
]);
59+
60+
const { getByText, queryByTestId } = render(<ProfileLinkChip uuid="test-nonexistent-uuid" />, {
61+
wrapper: Router,
62+
});
63+
64+
expect(getByText("http://localhost/profile/test-nonexistent-uuid")).toBeInTheDocument();
65+
expect(queryByTestId("mention-chip")).not.toBeInTheDocument();
66+
});
67+
68+
it("should render the localized profile link if the lookup fails", () => {
69+
mockUseProfileLookup.mockReturnValue([
70+
{ status: LookupStatus.Error, profile: null },
71+
jest.fn(),
72+
jest.fn(),
73+
]);
74+
75+
const { getByText, queryByTestId } = render(<ProfileLinkChip uuid="test-user-handle" />, {
76+
wrapper: Router,
77+
});
78+
79+
expect(getByText("http://localhost/profile/test-user-handle")).toBeInTheDocument();
80+
expect(queryByTestId("mention-chip")).not.toBeInTheDocument();
81+
});
82+
83+
it("should render the localized profile link if the lookup is pending", () => {
84+
mockUseProfileLookup.mockReturnValue([
85+
{ status: LookupStatus.Loading, profile: null },
86+
jest.fn(),
87+
jest.fn(),
88+
]);
89+
90+
const { getByText, queryByTestId } = render(<ProfileLinkChip uuid="test-user-handle" />, {
91+
wrapper: Router,
92+
});
93+
94+
expect(getByText("http://localhost/profile/test-user-handle")).toBeInTheDocument();
95+
expect(queryByTestId("mention-chip")).not.toBeInTheDocument();
96+
});
97+
});

src/components/ProfileLinkChip/index.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@ type Props = {
88
};
99

1010
/**
11-
* A profile UUID chip that returns a `@mention` chip
12-
* if the user exists. Otherwise, returns a localized
13-
* link to the profile.
14-
*
15-
* Not to be confused with the `<MentionChip />` itself, which
16-
* is used when `@username` is in the post body.
11+
* A profile embed chip used to pretty-link to a user's profile when a full profile link
12+
* is in a message.
1713
*
14+
* @note This is the opposite of `<MentionChip />`, which is used when a username is known.
1815
* @param {Props} props
1916
* @returns {JSX.Element}
2017
*/
@@ -33,6 +30,7 @@ const ProfileLinkChip: FC<Props> = ({ uuid }: Props) => {
3330
to={`/profile/${uuid}`}
3431
size="small"
3532
data-testid="mention-chip"
33+
clickable
3634
/>
3735
);
3836
};

0 commit comments

Comments
 (0)