Skip to content

Commit

Permalink
Add search API handler and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yasuflatland-lf committed Jul 2, 2024
1 parent a0815bc commit 4308b43
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 3 deletions.
136 changes: 136 additions & 0 deletions frontend/src/components/TopMenu.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// __tests__/TopMenu.test.tsx
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";
import React from "react";
import TopMenu from "./TopMenu"; // Adjust the import path as necessary
import { BrowserRouter } from "react-router-dom";
import { vi } from "vitest";

// Mock fetch
global.fetch = vi.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ results: [] }),
}),
);

describe("TopMenu Component", () => {
beforeEach(() => {
vi.clearAllMocks();
});

test("renders search input and settings button", () => {
render(
<BrowserRouter>
<TopMenu />
</BrowserRouter>,
);

const inputElement = screen.getByPlaceholderText(/search.../i);
const settingsButton = screen.getByRole("button");
const searchIcon = screen.getByTestId("search-icon");
const settingsIcon = screen.getByTestId("settings-icon");

expect(inputElement).toBeInTheDocument();
expect(settingsButton).toBeInTheDocument();
expect(searchIcon).toBeInTheDocument();
expect(settingsIcon).toBeInTheDocument();
});

test("updates search term on input change", () => {
render(
<BrowserRouter>
<TopMenu />
</BrowserRouter>,
);

const inputElement = screen.getByPlaceholderText(/search.../i);

fireEvent.change(inputElement, { target: { value: "test" } });

expect(inputElement).toHaveValue("test");
});

test("calls API on Enter key press", async () => {
render(
<BrowserRouter>
<TopMenu />
</BrowserRouter>,
);

const inputElement = screen.getByPlaceholderText(/search.../i);

fireEvent.change(inputElement, { target: { value: "test" } });
fireEvent.keyDown(inputElement, { key: "Enter", code: "Enter" });

await waitFor(() => expect(global.fetch).toHaveBeenCalledTimes(1));
expect(global.fetch).toHaveBeenCalledWith(
"https://api.example.com/search",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query: "test" }),
},
);
});

test("handles API call failure gracefully", async () => {
(global.fetch as jest.Mock).mockImplementationOnce(() =>
Promise.resolve({
ok: false,
}),
);

render(
<BrowserRouter>
<TopMenu />
</BrowserRouter>,
);

const inputElement = screen.getByPlaceholderText(/search.../i);

fireEvent.change(inputElement, { target: { value: "test" } });
fireEvent.keyDown(inputElement, { key: "Enter", code: "Enter" });

const consoleErrorSpy = vi
.spyOn(console, "error")
.mockImplementation(() => {});

await waitFor(() => expect(global.fetch).toHaveBeenCalledTimes(1));

await waitFor(() =>
expect(consoleErrorSpy).toHaveBeenCalledWith("API call failed"),
);

consoleErrorSpy.mockRestore();
});

test("handles network error gracefully", async () => {
(global.fetch as jest.Mock).mockImplementationOnce(() =>
Promise.reject(new Error("Network error")),
);

render(
<BrowserRouter>
<TopMenu />
</BrowserRouter>,
);

const inputElement = screen.getByPlaceholderText(/search.../i);

fireEvent.change(inputElement, { target: { value: "test" } });
fireEvent.keyDown(inputElement, { key: "Enter", code: "Enter" });

const consoleErrorSpy = vi
.spyOn(console, "error")
.mockImplementation(() => {});

await waitFor(() =>
expect(consoleErrorSpy).toHaveBeenCalledWith("Error:", expect.any(Error)),
);

consoleErrorSpy.mockRestore();
});
});
41 changes: 38 additions & 3 deletions frontend/src/components/TopMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
import React from "react";
import React, { useState } from "react";
import { IoIosSearch, IoIosSettings } from "react-icons/io";
import { Link } from "react-router-dom";

function TopMenu() {
const [searchTerm, setSearchTerm] = useState("");

const handleKeyDown = async (event) => {
if (event.key === "Enter") {
// Call the REST API
try {
const response = await fetch("https://api.example.com/search", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query: searchTerm }),
});

if (response.ok) {
const data = await response.json();
console.log(data); // Handle the response data
} else {
console.error("API call failed");
}
} catch (error) {
console.error("Error:", error);
}
}
};

return (
<div className="bg-white border border-gray-200">
<div className="fixed top-0 w-full flex justify-end py-2.5 pr-4 z-50">
<button className="text-white bg-transparent border-none text-base cursor-pointer hover:text-gray-400">
<Link to="/settings">
<IoIosSettings className="text-gray-700 text-2xl" />
<IoIosSettings
className="text-gray-700 text-2xl"
data-testid="settings-icon"
/>
</Link>
</button>
</div>
Expand All @@ -18,9 +47,15 @@ function TopMenu() {
type="text"
className="w-full pl-10 pr-4 py-2 border border-gray-500 rounded-lg focus:outline focus:border-gray-700 text-white placeholder-gray-500"
placeholder="Search..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onKeyDown={handleKeyDown}
/>
<span className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
<IoIosSearch className="focus:bg-gray-700" />
<IoIosSearch
className="focus:bg-gray-700"
data-testid="search-icon"
/>
</span>
</div>
</div>
Expand Down

0 comments on commit 4308b43

Please sign in to comment.