为什么jest找不到页面上的元素?

h43kikqp  于 11个月前  发布在  Jest
关注(0)|答案(2)|浏览(163)

我正在构建一个twitter克隆应用程序,我正在使用TDD。我已经到了尝试测试用户是否能够编辑tweet的地步。其想法是,如果用户单击当前tweet,它将再次显示输入,以便您可以根据需要编辑当前tweet。然后您再次单击tweet按钮,它将设置tweet。
我已经在Storybook中手动测试过了,它的工作原理和我预期的一样,但是,我无法让我的jest测试通过。Jest在测试失败时打印DOM,它仍然显示“我的第一条推文”在DOM中。我猜这是一个时间问题,但是我不知道如何修复它。我试过actwaitFor,但是这两个都不起作用。我做错了什么?

渲染我的组件

beforeEach(() => {
    renderResult = render(
      <Tweet profilePic={imageUrl} username="ndland" title="Tweet Title" />,
    );
  });

字符串

测试编辑推文

describe("editing a tweet", () => {
    const user = userEvent.setup();
    let input: Element;

    beforeEach(async () => {
      input = screen.getByPlaceholderText("What's happening?");
      const button = screen.getByRole("button", { name: "Tweet" });

      await act(async () => {
        await user.click(input);
        await user.keyboard("My First Tweet");

        await user.click(button);
      });
    });

    it("should allow a user to edit their tweet", async () => {
      const twxxt = screen.getByText("My First Tweet");
      expect(twxxt).toBeInTheDocument();
      expect(screen.queryByText("My edited Tweet")).not.toBeInTheDocument();

      await user.click(twxxt);
      expect(input).toHaveValue("My First Tweet");

      await user.keyboard("My edited Tweet");
      await user.click(screen.getByRole("button", { name: "Tweet" }));
      await waitFor(() => expect(input).toHaveValue("My edited Tweet"));
    });
  });

问题组件

const Tweet: React.FC<TweetProps> = ({ profilePic, username, title }) => {
  const [input, setInput] = React.useState<string | undefined>(undefined);
  const [tweet, setTweet] = React.useState<string | undefined>(undefined);
  const [placeholder, setPlaceholder] = React.useState<string | undefined>(
    "What's happening?",
  );
  const [placeholderColor, setPlaceholderColor] = React.useState<string>(
    "placeholder:text-slate-400",
  );
  const [likes, setLikes] = React.useState<number>(0);
  const [hasLiked, setHasLiked] = React.useState<boolean>(false);

  const handleTweet = () => {
    if (!input) {
      setPlaceholder("Enter a twxxt");
      setPlaceholderColor("placeholder:text-red-500");
    }
    setTweet(input);
  };

  const handleLike = () => {
    if (hasLiked) {
      setLikes(likes - 1);
      setHasLiked(false);
      return;
    }
    if (input && !hasLiked) {
      setLikes(likes + 1);
      setHasLiked(true);
    }
  };

  const handleTwxxtClick = () => {
    setPlaceholder(tweet);
    setTweet(undefined);
  };

  return (
    <div className="dark:bg-slate-800 mx-auto m-4 max-w-md overflow-hidden rounded-xl bg-white shadow-md md:max-w-2xl p-4 flex flex-row space-x-8">
      <div className="relative w-16 h-16 flex-shrink-0">
        <div>
          <Image
            className="rounded-full h-full w-full object-cover absolute"
            src={profilePic}
            alt="Profile Picture"
            width={100} // note: see https://nextjs.org/docs/app/building-your-application/optimizing/images#remote-images for more info
            height={100}
          />
        </div>
      </div>
      <div className="flex-grow">
        <div
          aria-label="username"
          className="text-indigo-500 font-semibold md:text-lg py-2"
        >
          @{username}
        </div>
        <div aria-label="title" className="dark:text-white font-semibold">
          {title}
        </div>
        {tweet ? (
          <>
            <div
              onClick={handleTwxxtClick}
              className="dark:text-white py-4 hover:cursor-pointer"
            >
              {tweet}
            </div>
            <div className="flex justify-normal space-x-2 items-center">
              {likes === 0 ? (
                <FontAwesomeIcon
                  aria-label="like"
                  onClick={handleLike}
                  className="text-white"
                  size="xl"
                  icon={outlineThumb}
                />
              ) : (
                <FontAwesomeIcon
                  aria-label="like"
                  onClick={handleLike}
                  className="text-white"
                  size="xl"
                  icon={solidThumb}
                />
              )}

              {likes !== 0 && (
                <div aria-label="likes" className="text-gray-400">
                  Likes: {likes}
                </div>
              )}
            </div>
          </>
        ) : (
          <>
            <input
              type="text"
              placeholder={placeholder}
              value={tweet}
              onChange={(e) => setInput(e.target.value)}
              className={`w-full my-2 p-2 rounded-md dark:bg-slate-800 dark:text-white dark:border-2 dark:border-indigo-800 ${placeholderColor}`}
            />
            <div className="flex justify-end items-center">
              <button
                onClick={handleTweet}
                className="bg-indigo-500 text-white rounded-full px-4 py-2"
              >
                Tweet
              </button>
            </div>
          </>
        )}
      </div>
    </div>
  );
};

export default Tweet;

edit忘记添加测试失败。

●推文›编辑推文›应该允许用户编辑他们的推文

expect(element).toHaveValue(My edited Tweet)

Expected the element to have value:
  My edited Tweet
Received:
  My First Tweet

edit 2:我在运行测试时看到以下输出:

<input
            aria-label="tweet input"
            class="w-full my-2 p-2 rounded-md dark:bg-slate-800 dark:text-white dark:border-2 dark:border-indigo-800 placeholder:text-slate-400"
            placeholder="My First Tweet"
            type="text"
            value=""
          />


这告诉我它正在命中处理程序,但是当我调用user.keyboard时,它并没有将这些键发送给input。我还添加了@杨柳建议的行,所以input现在应该有焦点。

qncylg1j

qncylg1j1#

正如我在评论中提到的,试试这个:

it("should allow a user to edit their tweet", async () => {
      const twxxt = screen.getByText("My First Tweet");
      expect(twxxt).toBeInTheDocument();
      expect(screen.queryByText("My edited Tweet")).not.toBeInTheDocument();

      await user.click(twxxt);
      expect(input).toHaveValue("My First Tweet");

      await user.click(input); // just added this line
      await user.keyboard("My edited Tweet");
      await user.click(screen.getByRole("button", { name: "Tweet" }));
      await waitFor(() => expect(input).toHaveValue("My edited Tweet"));
    });

字符串

jvidinwx

jvidinwx2#

好吧,看起来问题是,我为我的Assert引用了错误的对象。我引用的input对象仍然持有原始值'My First Tweet'。下面是修改后的测试,它按预期通过:

describe("editing a tweet", () => {
    const MY_FIRST_TWEET = "My First Tweet";
    const MY_EDITED_TWEET = "My Edited Tweet";
    const user = userEvent.setup();

    beforeEach(async () => {
      await user.click(tweetInput());

      await user.keyboard(MY_FIRST_TWEET);

      await user.click(tweetButton());
    });

    it("should allow a user to edit their tweet", async () => {
      const twxxt = screen.getByText(MY_FIRST_TWEET);
      expect(twxxt).toBeInTheDocument();

      await user.click(twxxt);

      await user.click(tweetInput());

      expect(tweetButton()).toBeInTheDocument();

      await user.keyboard(MY_EDITED_TWEET);

      await user.click(tweetButton());

      expect(screen.getByText(MY_EDITED_TWEET)).toHaveTextContent(
        MY_EDITED_TWEET,
      );
    });
  });
});

const tweetInput = (): HTMLElement => {
  return screen.getByLabelText("tweet input");
};

const tweetButton = (): HTMLElement => {
  return screen.getByRole("button", { name: /tweet/i });
};

字符串
在这个例子中,我只是把screen.getBy...拉到一个帮助函数中,所以我可以类似地重用代码,但是现在,当我需要更新对象时,我可以很容易地做到这一点。一个简单问题的简单解决方案,只是我的疏忽。希望这能有所帮助。

相关问题