Skip to content

Commit

Permalink
Merge pull request #684 from bounswe/FRONTEND-680
Browse files Browse the repository at this point in the history
Frontend 680
  • Loading branch information
alitariksahin authored Nov 25, 2024
2 parents be936e9 + bb0d1a9 commit 068596f
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 181 deletions.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@eslint/js": "^9.11.1",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/js-cookie": "^3.0.6",
"@types/react": "^18.3.10",
"@types/react-dom": "^18.3.0",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/post/compose-post-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const ComposePostButton = () => {
return (
<div
onClick={() => navigate("/compose-post")}
data-testid="compose-button"
color="primary"
aria-label="Post"
className="flex items-center justify-center shadow-xl fixed right-6 bottom-6 rounded-full p-4 bg-blue-600 hover:bg-blue-500 cursor-pointer"
Expand Down
76 changes: 49 additions & 27 deletions frontend/src/components/post/compose-post-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
useDisclosure,
Select,
SelectItem,
Selection,
} from "@nextui-org/react";

import PostCard from "./post-card.tsx";
Expand All @@ -21,8 +22,8 @@ import { AuthActions } from "../auth/utils.tsx";
import { useNavigate } from "react-router-dom";

const Tags = [
"#Vocabulary",
"#Grammar",
"#Vocabulary",
"#Vocabulary Tips",
"#Cultural Insights",
"#Idioms & Expressions",
Expand All @@ -32,51 +33,52 @@ const Tags = [
"#General",
"#Fun",
];
const DifficultyTags = ["#A1", "#A2", "#B1", "#B2", "#C1", "#C2",];

const DifficultyTags = ["#A1", "#A2", "#B1", "#B2", "#C1", "#C2"];

export default function ComposePostForm() {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [diffTag, setDiffTag] = useState<string>("");
const { isOpen, onOpen, onOpenChange } = useDisclosure();
const navigate = useNavigate();

const handleTagClick = (tag: string) => {
if (selectedTags.includes(tag)) {
setSelectedTags(selectedTags.filter((t) => t !== tag));
} else {
setSelectedTags([...selectedTags, tag]);
}
const handleCategorySelection = (keys: Selection) => {
setSelectedTags(Array.from(keys) as string[]);
};
const [diffTag, setDiffTag] = useState<string>("");
const handleDiffTagClick = (tag: string) => {
setDiffTag((prevTag) => (prevTag === tag ? "" : tag)); // Toggle diffTag

const handleDifficultySelection = (keys: Selection) => {
const selectedKey = Array.from(keys)[0] as string;
setDiffTag(selectedKey || "");
};

const handleSubmit = () => {
const handleSubmit = () => {
const { getToken } = AuthActions();
const token = getToken("access");
const token = getToken("access");

axios
.post(`${BASE_URL}/post/create/`, {
title: title,
description: content,
tags: [...selectedTags, ...(diffTag ? [diffTag] : [])],
}, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
.post(
`${BASE_URL}/post/create/`,
{
title: title,
description: content,
tags: [...selectedTags, ...(diffTag ? [diffTag] : [])],
},
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
}
})
)
.then((response) => {
navigate("/forum");
console.log(response.data);
})
.catch((error) => {
console.log(error);
});
}
};

return (
<div className="flex justify-center items-center h-full overflow-hidden">
Expand All @@ -89,6 +91,7 @@ export default function ComposePostForm() {
Title
</label>
<Input
data-testid="post-title-input"
placeholder="Enter the post title"
value={title}
onValueChange={setTitle}
Expand All @@ -99,6 +102,7 @@ export default function ComposePostForm() {
Content
</label>
<Textarea
data-testid="post-content-input"
placeholder="Write your post content here..."
value={content}
onValueChange={setContent}
Expand All @@ -113,23 +117,39 @@ export default function ComposePostForm() {
</label>
<div className="flex flex-row justify-between mb-3">
<Select
data-testid="difficulty-select"
label="Difficulty"
placeholder="Optional"
className="w-48 text-black"
selectedKeys={diffTag ? [diffTag] : []}
onSelectionChange={handleDifficultySelection}
>
{DifficultyTags.map((tag) => (
<SelectItem onPress={() => handleDiffTagClick(tag)} key={tag}>{tag}</SelectItem>
<SelectItem
data-testid={`difficulty-option-${tag}`}
key={tag}
>
{tag}
</SelectItem>
))}
</Select>
<Select
data-testid="category-select"
isRequired
label="Categories"
placeholder="Required Field"
selectionMode="multiple"
className="w-48 text-black"
selectedKeys={new Set(selectedTags)}
onSelectionChange={handleCategorySelection}
>
{Tags.map((tag) => (
<SelectItem onPress={() => handleTagClick(tag)} key={tag}>{tag}</SelectItem>
<SelectItem
key={tag}
data-testid={`category-option-${tag.replace("#", "")}`}
>
{tag}
</SelectItem>
))}
</Select>
</div>
Expand All @@ -138,6 +158,7 @@ export default function ComposePostForm() {

<div className="flex justify-end gap-4">
<Button
data-testid="preview-button"
onPress={onOpen}
color="default"
isDisabled={!title || !content || selectedTags.length === 0}
Expand Down Expand Up @@ -167,6 +188,7 @@ export default function ComposePostForm() {
</ModalContent>
</Modal>
<Button
data-testid="submit-post-button"
color="primary"
onPress={handleSubmit}
isDisabled={!title || !content || selectedTags.length === 0}
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/pages/forum.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import Forum from "./forum";
import { getByTestId, render, waitFor } from "@testing-library/react";
import { render, waitFor } from "@testing-library/react";
import { screen } from "@testing-library/dom";

vi.mock("react-router-dom", () => ({
useNavigate: () => vi.fn(),
useLocation: () => ({ pathname: "/forum" }),
Link: ({ children, to }: { children: React.ReactNode; to: string }) => (
<a href={to} data-testid="link">
{children}
</a>
),
}));

describe("Forum", () => {
Expand Down Expand Up @@ -51,3 +56,4 @@ describe("Forum", () => {
});
});
});

3 changes: 1 addition & 2 deletions frontend/src/pages/forum.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ export default function Forum() {
const [isLoading, setIsLoading] = useState(true);
const { getToken } = AuthActions();
const token = getToken("access");

const [sortFilter, setSortFilter] = useState<string>("Most Recent");
const handleSelectionChange = (e) => {
const handleSelectionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setSortFilter(e.target.value);
};

Expand Down
119 changes: 119 additions & 0 deletions frontend/src/pages/forumCompose.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { render, fireEvent, waitFor } from "@testing-library/react";
import { screen } from "@testing-library/dom";
import userEvent from "@testing-library/user-event";
import ComposePostForm from "../components/post/compose-post-form";
import { NextUIProvider } from "@nextui-org/react";
import { BrowserRouter } from "react-router-dom";
import { vi } from "vitest";
import axios from "axios";

// Mock modules
vi.mock("axios", () => ({
default: {
post: vi.fn().mockResolvedValue({ data: {} }),
},
}));

vi.mock("react-router-dom", async () => {
const actual = await vi.importActual("react-router-dom");
return {
...actual,
useNavigate: () => vi.fn(),
};
});

vi.mock("../components/auth/utils", () => ({
AuthActions: () => ({
getToken: () => "mock-token",
}),
}));

describe("ComposePostForm", () => {
const renderComponent = () => {
return render(
<NextUIProvider>
<BrowserRouter>
<ComposePostForm />
</BrowserRouter>
</NextUIProvider>
);
};

beforeEach(() => {
renderComponent();
});

it("renders form elements", () => {
expect(screen.getByTestId("post-title-input")).toBeInTheDocument();
expect(screen.getByTestId("post-content-input")).toBeInTheDocument();
expect(screen.getByTestId("category-select")).toBeInTheDocument();
expect(screen.getByTestId("difficulty-select")).toBeInTheDocument();
expect(screen.getByTestId("submit-post-button")).toBeInTheDocument();
});

it("handles input changes", () => {
const titleInput = screen.getByTestId("post-title-input");
const contentInput = screen.getByTestId("post-content-input");

fireEvent.change(titleInput, { target: { value: "Test Title" } });
fireEvent.change(contentInput, { target: { value: "Test Content" } });

expect(titleInput).toHaveValue("Test Title");
expect(contentInput).toHaveValue("Test Content");
});

it("disables submit button when form is invalid", () => {
const submitButton = screen.getByTestId("submit-post-button");
expect(submitButton).toBeDisabled();
});

it("enables submit button when form is valid", async () => {
const user = userEvent.setup();

const titleInput = screen.getByTestId("post-title-input");
await user.type(titleInput, "Test Title");

const contentInput = screen.getByTestId("post-content-input");
await user.type(contentInput, "Test Content");

const categorySelect = screen.getByTestId("category-select");
await user.click(categorySelect);

const grammarOption = await screen.findByTestId("category-option-Grammar");
await user.click(grammarOption);

await waitFor(() => {
const submitButton = screen.getByTestId("submit-post-button");
expect(submitButton).not.toBeDisabled();
});
});

it("handles form submission", async () => {
const user = userEvent.setup();

await user.type(screen.getByTestId("post-title-input"), "Test Title");
await user.type(screen.getByTestId("post-content-input"), "Test Content");

await user.click(screen.getByTestId("category-select"));
const grammarOption = await screen.findByTestId("category-option-Grammar");
await user.click(grammarOption);

const submitButton = screen.getByTestId("submit-post-button");
await waitFor(() => {
expect(submitButton).not.toBeDisabled();
});
await user.click(submitButton);

await waitFor(() => {
expect(axios.post).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
title: "Test Title",
description: "Test Content",
tags: expect.arrayContaining(["#Grammar"]),
}),
expect.any(Object)
);
});
});
});
Loading

0 comments on commit 068596f

Please sign in to comment.