Задать вопрос

Как решить проблему с рендером в тестах с асинхронным кодом?

Всем доброго времени суток, возникла проблема,
Написал тест для детальной страницы
import { describe, it, expect } from "vitest";
import RenderWithRouter from "../tests/helpers/renderWithRouter.tsx";
import { screen } from "@/utils/test-utils.tsx";
import { act } from "react-dom/test-utils";

describe("CLIPS PAGE", () => {
  it("Рендер спика роликов", async () => {
    RenderWithRouter({
      initialEntries: ["/clips"],
    });

    const clipsAudioItems = await screen.findAllByTestId("clips/audioItem");
    const ListItemsTemplate = await screen.findByTestId("ListItemsTemplate");
    expect(ListItemsTemplate).toBeInTheDocument();
    expect(clipsAudioItems.length).toBe(5);
  });

  it("Рендер детальной страницы", async () => {
    RenderWithRouter({
      initialEntries: ["/clips/1482722015105"],
    });

    // Добавьте этот блок с использованием act
    await act(async () => {
      const titleDetailPage = await screen.findByTestId("detailPageTitle");
      console.log(titleDetailPage);
    });
  });
});


Вот сам компонент, который отрировывается
import ContentDetailPage from "@/modules/DetailClip/components/ContentDetailPage/ContentDetailPage.tsx";
import { observer } from "mobx-react-lite";
import Loader from "@/components/Loader/Loader.tsx";
import classes from "./DetailClipWrapper.module.scss";
import { useAppStore } from "@/stores/useAppStore.ts";

const DetailClipWrapper = observer(() => {
  const store = useAppStore();

  return (
    <section className={classes.section}>
      {store.detailClipStore.loading ? (
        <Loader className={classes.preloader} />
      ) : (
        <ContentDetailPage />
      )}
    </section>
  );
});

export default DetailClipWrapper;


Вот хелпер
import { MemoryRouter } from "react-router-dom";
import AppRouter from "../../router/AppRouter.tsx";
import { render, RenderResult } from "@utils/test-utils.tsx";
import React from "react";
import { AppStoreProvider } from "../../stores/AppStoreProvider.tsx";

interface IProps {
  component?: React.FC;
  initialEntries?: string[];
}

const renderWithRouter = ({
  component: Component,
  initialEntries = ["/"],
}: IProps): RenderResult => {
  return render(
    <MemoryRouter initialEntries={initialEntries}>
      <AppStoreProvider>
        <AppRouter />
        {Component && <Component />}
      </AppStoreProvider>
    </MemoryRouter>
  );
};

export default renderWithRouter;


А вот reducer mobx с которым работает компонент
import { makeAutoObservable } from "mobx";
import {
  IDetailClipData,
  DetailClipInterfaces,
} from "@/modules/DetailClip/types/DetailClipInterfaces.ts";
import { DetailClipMock } from "@/modules/DetailClip/mocks/DetailClipMock.ts";

class DetailClipStore implements DetailClipInterfaces {
  data: IDetailClipData | null = null;
  loading = false;
  error: Error | null = null;

  constructor() {
    makeAutoObservable(this);
  }

  // Получение детальной информации об аудио с сервера
  async fetchAudioData(/*id: number*/) {
    try {
      this.setLoading(true);
      this.error = null;
      // Имитация запроса к серверу
      await new Promise((resolve) => setTimeout(resolve, 2000));

      this.setData(DetailClipMock);
      // обработка данных из за ассинхронности
    } catch (error: unknown) {
      if (error instanceof Error) {
        this.error = error;
      }
    } finally {
      this.setLoading(false);
    }
  }

  setData(data: IDetailClipData) {
    this.data = data;
  }

  setLoading(loading: boolean) {
    this.loading = loading;
  }
}

export default new DetailClipStore();


Проблема в том, что онрендерит Loading, но не ждет когда fetchAudioData закончит свой функционал.
Но когда убираешь строчку когда
await new Promise((resolve) => setTimeout(resolve, 2000));

Все работает, как победить эту проблему?
пробовал решение с act - не получилось
  • Вопрос задан
  • 48 просмотров
Подписаться 1 Простой 2 комментария
Пригласить эксперта
Ответы на вопрос 1
Alexandroppolus
@Alexandroppolus
кодир
Самый православный вариант - в AppStoreProvider для тестов подставлять вместо реального DetailClipStore заглушку, которая реализует тот же интерфейс. У тебя ведь тест на реакт-компонент? Вот и незачем туда тащить DetailClipStore (попутно тестируя и его тоже). На него лучше написать отдельный тест, в котором проверить чисто логику.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы