Skip to content

Commit c8d996a

Browse files
authored
Merge pull request #23 from guanquann/frontend-testing
Add frontend testing
2 parents 2246f81 + 13f909d commit c8d996a

File tree

14 files changed

+740
-126
lines changed

14 files changed

+740
-126
lines changed

frontend/package-lock.json

Lines changed: 63 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@types/react-dom": "^18.3.0",
4040
"@types/uuid": "^10.0.0",
4141
"@vitejs/plugin-react": "^4.3.1",
42+
"babel-jest": "^29.7.0",
4243
"eslint": "^9.9.0",
4344
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
4445
"eslint-plugin-react-refresh": "^0.4.9",
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { fireEvent, render, screen } from "@testing-library/react";
2+
import "@testing-library/jest-dom";
3+
import QuestionCategoryAutoComplete from ".";
4+
5+
describe("Question Category Auto Complete", () => {
6+
const selectedCategories: string[] = ["DFS"];
7+
const setSelectedCategories = jest.fn();
8+
9+
it("Question Category Auto Complete is rendered", () => {
10+
render(
11+
<QuestionCategoryAutoComplete
12+
selectedCategories={selectedCategories}
13+
setSelectedCategories={setSelectedCategories}
14+
/>
15+
);
16+
17+
const category = screen.getByText("DFS");
18+
19+
expect(category).toBeInTheDocument();
20+
});
21+
22+
it("Adding a new category from the category list", () => {
23+
render(
24+
<QuestionCategoryAutoComplete
25+
selectedCategories={selectedCategories}
26+
setSelectedCategories={setSelectedCategories}
27+
/>
28+
);
29+
30+
const input = screen.getByLabelText("Category");
31+
fireEvent.change(input, { target: { value: "Strings" } });
32+
33+
expect(screen.getByText("Strings")).toBeInTheDocument();
34+
});
35+
36+
it("Adding a new category not from the category list", () => {
37+
const { rerender } = render(
38+
<QuestionCategoryAutoComplete
39+
selectedCategories={selectedCategories}
40+
setSelectedCategories={setSelectedCategories}
41+
/>
42+
);
43+
44+
const input = screen.getByLabelText("Category");
45+
fireEvent.change(input, { target: { value: "New Category" } });
46+
47+
const valueAdded = 'Add: "New Category"';
48+
expect(screen.getByText(valueAdded)).toBeInTheDocument();
49+
50+
fireEvent.click(screen.getByText(valueAdded));
51+
52+
const updatedCategories = [...selectedCategories, "New Category"];
53+
54+
rerender(
55+
<QuestionCategoryAutoComplete
56+
selectedCategories={updatedCategories}
57+
setSelectedCategories={setSelectedCategories}
58+
/>
59+
);
60+
61+
expect(screen.getByText("New Category")).toBeInTheDocument();
62+
});
63+
64+
it("Remove a category from selected categories", () => {
65+
render(
66+
<QuestionCategoryAutoComplete
67+
selectedCategories={selectedCategories}
68+
setSelectedCategories={setSelectedCategories}
69+
/>
70+
);
71+
72+
const deleteButton = screen.getByTestId("CancelIcon");
73+
fireEvent.click(deleteButton);
74+
75+
expect(setSelectedCategories).toHaveBeenCalledWith([]);
76+
});
77+
});

frontend/src/components/QuestionCategoryAutoComplete/index.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ interface QuestionCategoryAutoCompleteProps {
77
setSelectedCategories: React.Dispatch<React.SetStateAction<string[]>>;
88
}
99

10-
const QuestionCategoryAutoComplete: React.FC<QuestionCategoryAutoCompleteProps> = ({
11-
selectedCategories,
12-
setSelectedCategories,
13-
}) => {
10+
const QuestionCategoryAutoComplete: React.FC<
11+
QuestionCategoryAutoCompleteProps
12+
> = ({ selectedCategories, setSelectedCategories }) => {
1413
// TODO
1514
// Fetch category list from the server
1615

@@ -25,7 +24,8 @@ const QuestionCategoryAutoComplete: React.FC<QuestionCategoryAutoCompleteProps>
2524
sx={{ marginTop: 2 }}
2625
value={selectedCategories}
2726
onChange={(e, newCategoriesSelected) => {
28-
const newValue = newCategoriesSelected[newCategoriesSelected.length - 1];
27+
const newValue =
28+
newCategoriesSelected[newCategoriesSelected.length - 1];
2929
if (typeof newValue === "string" && newValue.startsWith(`Add: "`)) {
3030
const newCategory = newValue.slice(6, -1);
3131
categoryList.push(newCategory);
@@ -55,7 +55,8 @@ const QuestionCategoryAutoComplete: React.FC<QuestionCategoryAutoCompleteProps>
5555
size="small"
5656
label={
5757
typeof option === "string" && option.startsWith(`Add: "`)
58-
? option.slice(6, -1)
58+
? /* c8 ignore next */
59+
option.slice(6, -1)
5960
: option
6061
}
6162
key={key}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { fireEvent, render, screen } from "@testing-library/react";
2+
import "@testing-library/jest-dom";
3+
import QuestionImage from ".";
4+
5+
Object.assign(navigator, {
6+
clipboard: {
7+
writeText: jest.fn(),
8+
},
9+
});
10+
11+
describe("Question Image", () => {
12+
const url = "https://example.com/image.jpg";
13+
const mockHandleClickOpen = jest.fn();
14+
15+
it("Question Image is rendered", () => {
16+
render(<QuestionImage url={url} handleClickOpen={mockHandleClickOpen} />);
17+
18+
const image = screen.getByAltText("question image");
19+
20+
expect(image).toBeInTheDocument();
21+
});
22+
23+
it("Copy Question Image url", () => {
24+
render(<QuestionImage url={url} handleClickOpen={mockHandleClickOpen} />);
25+
26+
const copyButton = screen.getByLabelText("copy");
27+
fireEvent.click(copyButton);
28+
29+
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
30+
`![image](${url})`
31+
);
32+
});
33+
34+
it("Expand Question Image", () => {
35+
render(<QuestionImage url={url} handleClickOpen={mockHandleClickOpen} />);
36+
37+
const fullscreenButton = screen.getByLabelText("fullscreen");
38+
fireEvent.click(fullscreenButton);
39+
40+
expect(mockHandleClickOpen).toHaveBeenCalledWith(url);
41+
});
42+
});

frontend/src/components/QuestionImage/index.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ interface QuestionImageProps {
1010
handleClickOpen: (url: string) => void;
1111
}
1212

13-
const QuestionImage: React.FC<QuestionImageProps> = ({ url, handleClickOpen }) => {
13+
const QuestionImage: React.FC<QuestionImageProps> = ({
14+
url,
15+
handleClickOpen,
16+
}) => {
1417
return (
1518
<ImageListItem
1619
sx={{
@@ -27,7 +30,12 @@ const QuestionImage: React.FC<QuestionImageProps> = ({ url, handleClickOpen }) =
2730
<img
2831
src={url}
2932
loading="lazy"
30-
style={{ width: "100%", height: "100%", objectFit: "cover", borderRadius: 1 }}
33+
style={{
34+
width: "100%",
35+
height: "100%",
36+
objectFit: "cover",
37+
borderRadius: 1,
38+
}}
3139
alt="question image"
3240
/>
3341

@@ -54,11 +62,16 @@ const QuestionImage: React.FC<QuestionImageProps> = ({ url, handleClickOpen }) =
5462
toast.success("Image URL copied to clipboard");
5563
}}
5664
sx={{ color: "#fff" }}
65+
aria-label="copy"
5766
>
5867
<ContentCopyIcon />
5968
</IconButton>
6069

61-
<IconButton onClick={() => handleClickOpen(url)} sx={{ color: "#fff " }}>
70+
<IconButton
71+
onClick={() => handleClickOpen(url)}
72+
sx={{ color: "#fff " }}
73+
aria-label="fullscreen"
74+
>
6275
<FullscreenIcon />
6376
</IconButton>
6477
</Box>

0 commit comments

Comments
 (0)