Skip to content

Commit

Permalink
add test for autcomplete component
Browse files Browse the repository at this point in the history
  • Loading branch information
Kellswork committed Jan 14, 2024
1 parent 7c02135 commit b5f4713
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 30 deletions.
51 changes: 37 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@babel/plugin-transform-private-property-in-object": "^7.23.4",
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "13.4.0",
"@testing-library/user-event": "13.5.0",
Expand Down Expand Up @@ -39,5 +40,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11"
}
}
9 changes: 0 additions & 9 deletions src/App.test.tsx

This file was deleted.

180 changes: 180 additions & 0 deletions src/components/autoComplete.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import {
render,
screen,
fireEvent,
waitFor,
act,
within,
cleanup,
} from "@testing-library/react";
import AutoComplete from "./autoComplete";

afterEach(() => {
cleanup();
});

describe("AutoComplete Component", () => {
it("renders the input box", () => {
render(<AutoComplete />);
const searchInput = screen.getByLabelText("search-input");
expect(searchInput).toBeInTheDocument();
});

it("renders the suggestion list on input focus", async () => {
render(<AutoComplete />);

const suggestionList = screen.queryByTestId("suggestion-list");
expect(suggestionList).not.toBeInTheDocument();

const searchInput = screen.getByLabelText("search-input");

act(() => {
searchInput.focus();
});

await waitFor(
() => {
const showSuggestionList = screen.queryByTestId("suggestion-list");
expect(showSuggestionList).toBeInTheDocument();
},
{ timeout: 5000 }
);
});

it("hides the suggestion list on click outside the text box", async () => {
render(<AutoComplete />);

const suggestionList = screen.queryByTestId("suggestion-list");
expect(suggestionList).not.toBeInTheDocument();

const searchInput = screen.getByLabelText("search-input");

// show the suggestion list
act(() => {
searchInput.focus();
});

// confirm its showing
await waitFor(
() => {
const showSuggestionList = screen.getByTestId("suggestion-list");
expect(showSuggestionList).toBeInTheDocument();
},
{ timeout: 5000 }
);

// click out of the input box
fireEvent.click(document.body);

// check that the suggestion list is nolonger showiung
await waitFor(
() => {
const showSuggestionList = screen.queryByTestId("suggestion-list");
expect(showSuggestionList).not.toBeInTheDocument();
},
{ timeout: 5000 }
);
});

it("updates the suggestion list based on user input", async () => {
render(<AutoComplete />);

const searchInput = screen.getByLabelText("search-input");
const inputValue = "cle";

fireEvent.focus(searchInput);

fireEvent.change(searchInput, { target: { value: inputValue } });

await waitFor(
() => {
const suggestions = screen.getAllByTestId("suggestion-list");
const suggestionContent = suggestions.map((suggestion) =>
suggestion.textContent?.toLocaleLowerCase().includes(inputValue)
);
expect(suggestionContent).toBeTruthy();
},
{ timeout: 5000 }
);
});

it("populates the input field when a suggestion item is clicked", async () => {
render(<AutoComplete />);
const inputValue = "cle";
const searchInput = screen.getByLabelText(
"search-input"
) as HTMLInputElement;
let suggestionItems: HTMLElement[] = [];

fireEvent.change(searchInput, { target: { value: inputValue } });

await waitFor(
() => {
const suggestions = screen.queryByTestId(
"suggestion-list"
) as HTMLElement;
expect(suggestions).toBeInTheDocument();

suggestionItems = within(suggestions)
.getAllByRole("listitem")
.filter((value) =>
value.textContent?.toLocaleLowerCase().includes(inputValue)
);
},
{ timeout: 5000 }
);

fireEvent.click(suggestionItems[0]);

await waitFor(
() => {
const searchInput = screen.getByLabelText(
"search-input"
) as HTMLInputElement;

expect(searchInput.value).toBe("Clementine Bauch");
},
{ timeout: 5000 }
);
});

it("should return a div with no-options if no suggestion list is empty", async () => {
render(<AutoComplete />);
const inputValue = "les";
const searchInput = screen.getByLabelText(
"search-input"
) as HTMLInputElement;

fireEvent.change(searchInput, { target: { value: inputValue } });

await waitFor(
() => {
const noOption = screen.getByText("No options");
expect(noOption).toBeInTheDocument();
},
{ timeout: 5000 }
);
});

it("moves selected suggestion move and down when using the keyboard arrow keys", async () => {
render(<AutoComplete />);
const inputValue = "c";
const searchInput = screen.getByLabelText(
"search-input"
) as HTMLInputElement;
let suggestions: HTMLElement | null = null;

fireEvent.change(searchInput, { target: { value: inputValue } });

await waitFor(
() => {
const suggestions = screen.queryByTestId(
"suggestion-list"
) as HTMLElement;
expect(suggestions).toBeInTheDocument();
},
{ timeout: 5000 }
);
if (suggestions) fireEvent.keyDown(suggestions, { key: "ArrowDown" });
});
});
2 changes: 1 addition & 1 deletion src/components/autoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function AutoComplete() {

const handleOnClick = (event: React.MouseEvent<HTMLLIElement>) => {
setShowSuggestions(false);
setSearchText(event.currentTarget.innerText);
if(event.currentTarget.textContent) setSearchText(event.currentTarget.textContent);
};

const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
Expand Down
6 changes: 3 additions & 3 deletions src/components/autoCompleteState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ const AutoCompleteState = () => {
useEffect(() => {
(async () => {
const filteredData = await FileteredData(debouncedInputValue);
if (filteredData) {
setSuggestions(filteredData);
}
if (!filteredData) return

setSuggestions(filteredData);
return filteredData;
})();

Expand Down
1 change: 1 addition & 0 deletions src/components/inputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const InputField: FC<Props> = ({
<input
type="text"
placeholder="Search users"
aria-label="search-input"
ref={inputRef}
value={searchText}
onChange={handleInputChange}
Expand Down
7 changes: 4 additions & 3 deletions src/components/suggestionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ const SuggestionList: FC<Props> = ({
showSuggestions,
handleOnClick,
selectedSuggestion,
searchText
searchText,
}) => {
if (showSuggestions) {
return suggestions.length ? (
<ul className="suggestions">
<ul data-testid="suggestion-list" className="suggestions">
{suggestions.map((suggestion, index) => (
<li
data-testid={`suggestion-item-${index}`}
className={
selectedSuggestion === index ? "suggestion active" : "suggestion"
}
Expand All @@ -33,7 +34,7 @@ const SuggestionList: FC<Props> = ({
))}
</ul>
) : (
<div className="no-options">No options</div>
<div className="no-options" data-testid='no-options'>No options</div>
);
} else return null;
};
Expand Down

0 comments on commit b5f4713

Please sign in to comment.