1

I am struggling to understand why this component doesn't update after I click on it with userEvent. I have a search bar, when focused === true, it expands, and has another field "Move in". This is my test that doesn't work and I don't get the search bar expanded after clicking.

describe("Searchbar tests", () => {
  let SearchBar = (
    <DesktopSearch
      wrapper={wrap}
      handleKeyDown={jest.fn()}
      handleChange={jest.fn()}
      availability={undefined}
    />
  );

  it("should have move in once focused", async () => {
    render(SearchBar);
    const searchDiv = await screen.findByTestId("search-location");
    await userEvent.click(searchDiv);
    expect(screen.getByText("Move in")).toBeInTheDocument();
  });
});

screen.debug()

  <body>
      <div>
        <div
          class="sc-ksdxgE cXdeLl"
        >
          <div
            class="sc-hBUSln bQqsgK"
            data-testid="search-location"
          >
            <span
              style="width: 0px; opacity: 0;"
            >
              Search
            </span>
            <div>
              <span
                style="line-height: 0.5;"
              >
                Location
              </span>
              <input
                class="sc-fotOHu bfsfMe"
                value=""
              />
            </div>
            <div
              class="sc-fFeiMQ htNsUj"
              style="width: 6.49676%;"
            />
          </div>
          <svg
            class="sc-kfPuZi kGORHg"
            fill="currentColor"
            height="20"
            stroke="currentColor"
            stroke-width="0"
            viewBox="0 0 16 16"
            width="20"
            xmlns="http://www.w3.org/2000/svg"
          >
            <title>
              Search
            </title>
            <path
              d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0 0 13 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 0 0 0-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"
              fill-rule="evenodd"
            />
          </svg>
        </div>
      </div>
    </body>

Minimal example:

    import { SearchBarContext } from "context";
import { AnimatePresence, AnimateSharedLayout, motion } from "framer-motion";
import { ReactElement, useContext } from "react";
import { DeviceSearchProps } from ".";
import {
  AutocompleteContainer,
  SearchAvailability,
  SearchDate,
  SearchIcon,
  SearchInput,
  SearchLocation,
  SearchStyle,
} from "./desktopStyles";

    export default function DesktopSearch({
      className,
      wrapper,
      handleKeyDown: _handleKeyDown,
      handleChange: _handleChange,
      availability,
    }: DeviceSearchProps): ReactElement {
      const { focus, focused } = useContext(SearchBarContext);
      // [focused, setFocused] = useState(false),
      //focus = () => setFocused(true),
    
      return (
        <SearchStyle className={className} ref={wrapper}>
          <AnimateSharedLayout>
            <SearchLocation onClick={focus} layout>
              <AnimatePresence>
                <motion.span
                  initial={{ width: "0px" }}
                  animate={{
                    width: focused ? "0px" : "auto",
                    opacity: focused ? 0 : 1,
                  }}
                  transition={{ duration: 0.5, type: "string", delay: 0.2 }}
                >
                  Search
                </motion.span>
              </AnimatePresence>
              <div>
                <span style={{ lineHeight: 0.5 }}>Location</span>
                <SearchInput isFocused={focused} />
              </div>
              <AutocompleteContainer
                animate={{ width: focused ? "110%" : "90%" }}
                transition={{ duration: 0.2, type: "string" }}
              ></AutocompleteContainer>
            </SearchLocation>
    
            {focused && (
              <>
                <SearchDate>
                  <SearchAvailability
                    initial={{ opacity: 0 }}
                    animate={{ opacity: 1 }}
                    exit={{ opacity: 0 }}
                  >
                    {availability ? (
                      availability.toLocaleDateString("en-GB")
                    ) : false ? (
                      <>__ __ __</>
                    ) : (
                      "Move in"
                    )}
                  </SearchAvailability>
                </SearchDate>
              </>
            )}
            <SearchIcon size={20} />
          </AnimateSharedLayout>
        </SearchStyle>
      );

}
illyria
  • 324
  • 4
  • 19
  • Could you please call [screen.debug()](https://testing-library.com/docs/queries/about#screendebug) after clicking on the element and attach the result to your question? – Dzianis Roi Jun 14 '22 at 09:07
  • Without a [mre] including a minimal component, it's very hard to say. – jonrsharpe Jun 14 '22 at 10:04
  • I don't think `
    ` element can be focused without specifying [tabindex](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) to it. Or, as an alternative, you can assign `data-testid="search-location"` attribute to `input` element within the "search location" block
    – Dzianis Roi Jun 14 '22 at 10:09
  • Hey! Did you try to know is when doing the userEvent is really focused? Check this https://testing-library.com/docs/guide-events/#focusblur – S.Marx Jun 14 '22 at 10:29
  • Hey, @S.Marx I just tried it, and it doesn't work, unfortunately. Thank you, tho – illyria Jun 14 '22 at 10:47
  • How do you handle the focus === true? – S.Marx Jun 14 '22 at 10:54
  • I haven't tested it but you are testing the initial render in your test. Not the one after the button is click. You might have to wait for the text to be in the document with something like `await waitFor(() => { expect(screen.getByText("Move in")).toBeInTheDocument(); });` – Kevin Amiranoff Jun 14 '22 at 11:30
  • Hi @KevinAmiranoff I thought about it, and waited for it but I guess that's not the issue – illyria Jun 14 '22 at 11:37
  • 1
    @illyria, I may also assume that it doesn't work with context, because the context doesn't exist when you render the component with react testing library. Try to add a [wrapper](https://testing-library.com/docs/react-testing-library/api/#wrapper) to the render function to wrap the component by the context - `render(SearchBar, {wrapper: SearchBarContext.Provider})` – Dzianis Roi Jun 14 '22 at 12:47

2 Answers2

2

The problem in this code is that the context doesn't exist when you render the component with react testing library. Try to add a wrapper to the render function to wrap the component by the context

const WithContextProviders = ({ children }) => {
  return (
    <SearchBarContext.Provder {...providersPropsIfNeeded}>
      {children}
    </SearchBarContext.Provder>
  );
};

it("should have move in once focused", async () => {
  render(SearchBar, { wrapper: WithContextProviders });
  const searchDiv = await screen.findByTestId("search-location");
  await userEvent.click(searchDiv);
  expect(screen.getByText("Move in")).toBeInTheDocument();
});
Dzianis Roi
  • 853
  • 4
  • 12
0

Since I do not know how you handle the focus === true I've created a little example that maybe helps you:

I handle if is focused or not with a useState by default false and then when, in that case the button, is clicked changes to true Move in is displayed

import { useState } from "react";

function App() {
  const [isFocus, setIsFocus] = useState(false);

  const handleFocus = () => {
    setIsFocus(true);
  };

  return (
    <>
      <button onClick={handleFocus}>Display text when clicked</button>
      {isFocus ? <p>Move in</p> : ""}
    </>
  );
}  
export default App;

And this is the test, that passes:

test("focus should render", async () => {
  render(<App />);

  expect(screen.queryByText(/Move in/i)).not.toBeInTheDocument();

  fireEvent.click(screen.getByText(/Display text when clicked/i));
  expect(screen.getByText(/Move in/i)).toBeInTheDocument();
});

enter image description here

S.Marx
  • 977
  • 10
  • 14
  • Hey @S.Marx, I updated the question with the component, but either way, focus is just setFocused(true). I tried your code, but it doesn't work – illyria Jun 14 '22 at 11:09
  • How do you handle when is focused or not in your code? – S.Marx Jun 14 '22 at 11:12
  • it's just a usestate, defaulted to false, when I click it becomes true – illyria Jun 14 '22 at 11:13
  • How weird, it should work. Or add some moe code sho we can see – S.Marx Jun 14 '22 at 11:19
  • I've made some brief changes and the pass test screenshot, give it a try – S.Marx Jun 14 '22 at 11:28
  • Still nothing. The only thing I can think of is that focus and focused come from useContext, or I have no idea. Thank you though – illyria Jun 14 '22 at 11:37
  • Maybe you should `jest.fn()` the function thats sets focus to true – S.Marx Jun 14 '22 at 11:51
  • 1
    The issue apparently is the useContext. I changed focus and focused with local states there and it works. I guess I need to understand how to make it work with useContext. Thank you! – illyria Jun 14 '22 at 11:55