Как высмеять крючок useSearchParams NextJS в Cypress?

У меня есть компонент, который использует крючок useSearchParams из NextJS:


import { costCalculationApiUrl, solarAnalyticsApiHeader } from "@/constants/urls"
import { fetcher } from "@/utils/fetcher"
import { useSearchParams } from "next/navigation"
import { useEffect } from "react"
import { useDispatch, useSelector } from "react-redux"
import useSWRMutation from "swr/mutation"

import { RootState } from "@/redux/store"

import { CardDescription, CardTitle } from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
import { Skeleton } from "@/components/ui/skeleton"

import { CustomInstallationReportPreview } from "../../export-report/custom/components"
import { Invoice } from "../../offer/components"
import { Roof, updatePanelPlacementData } from "../../panel-installation/cutsom/state/slice"
import { format2APIDate } from "../helpers"
import { updateFinancialInsightsData } from "../state/slice"
import { AnalyticsForm } from "./form"
import { InvestementCard } from "./investement-card"
import { AnalyticsStats } from "./stats"

export const Analytics = () => {
  const searchParams = useSearchParams()

  const dispatch = useDispatch()

  const nominalPower = searchParams.get("wp")

  const selectedRoofs: Roof[] | undefined = useSelector((state: RootState) => state.customInstallation.selectedRoofs)

  const {
    feedInPrice,
    currentPrice,
    installationDate,
    maintanance,
    insurance,
    interest,
    creditPeriod,
    outsideCapital,
    inflation,
  } = useSelector((state: RootState) => state.financialInsights.formData)

  const { data: customInstallationData } = useSelector((state: RootState) => state.customInstallation)

  const url = `${costCalculationApiUrl}?feed_in_price=${feedInPrice}&used_price=${currentPrice}&installation_date=${format2APIDate(installationDate)}&maintenance_cost_kwp=${maintanance}&insurance_cost_kwp=${insurance}&interest_rate=${interest}&credit_period=${creditPeriod}&wp=${nominalPower}&outside_capital=${outsideCapital}&inflation=${inflation}&additional_grid_feed_in_annual=${customInstallationData?.additionalGridFeedAnnual}&additional_solar_consumption_annual_abs=${customInstallationData?.additionalSolarConsumptionAnnual}&additional_panels=${customInstallationData?.additionalPanels}`

  const { data, error, trigger, reset } = useSWRMutation(url, (url) => fetcher(url, solarAnalyticsApiHeader))

  useEffect(() => {
    if (customInstallationData) {
      reset()
      trigger().then((data) => {
        if (data) {
          const amortizationTime = data["Amortization Time"]
          const amortizationDate = data["Amortization Date"]
          const financialSaving = data["Accumulated Discounted period Cashflows after 20 years"]
          const investment = data["Investment"]
          const timeSeries = data["Time-Series"]

          // Update the state for the report
          // Though we pass data as prop to the components but the report have to access the state to display the data
          dispatch(
            updateFinancialInsightsData({
              amortizationDate,
              amortizationTime,
              financialSaving,
              investment,
              timeSeries,
            })
          )
        }
      })
    }

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [nominalPower, customInstallationData])

  // This effect is for the report
  // When the user access directly to the Analytics tab,
  // The panel placement is empty by default and it should be filled by selected roofs
  // The selected roofs is filled by roofs from the API after the first render of panel installation page
  useEffect(() => {
    dispatch(updatePanelPlacementData(selectedRoofs))
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selectedRoofs])

  if (error) {
    // Message: Something went wrong
    return <p>Etwas ist schief gelaufen</p>
  }

  if (!data) {
    return (
      <div className='flex flex-col justify-between gap-8 h-full px-8'>
        <Skeleton className='h-24 w-full' />
        <div className='flex w-full gap-8'>
          <Skeleton className='h-96 w-1/4' />
          <div className='flex flex-col w-3/4 h-fit gap-8'>
            <Skeleton className='w-full h-24' />
            <Skeleton className='w-full h-48' />
            <Skeleton className='w-full h-96' />
          </div>
        </div>
      </div>
    )
  } else {
    return (
      <div className='flex flex-col px-8'>
        <CardTitle>Wirtschaftlichkeitsanalyse</CardTitle>
        <CardDescription className='mt-1 mb-0 text-base'>
          Prüfen Sie Ihre Annahmen und spielen Sie Szenarien durch.
        </CardDescription>
        <div className='flex items-center gap-2 absolute right-6'>
          <Invoice />
          <CustomInstallationReportPreview />
        </div>
        <Separator className='my-4' />
        <div className='flex gap-16 mt-4 w-full items-stretch h-full overflow-hidden'>
          <div className='flex gap-8 w-full'>
            <div className='max-w-[480px] w-full'>
              <AnalyticsForm />
            </div>
            <div className='flex flex-col flex-grow content-stretch w-full gap-8 overflow-auto'>
              <InvestementCard data = {data} />
              <Separator />
              <AnalyticsStats data = {data} />
            </div>
          </div>
        </div>
      </div>
    )
  }
}

Я создал тестирование компонента, но не смог понять, как имитировать крючок useSearchParams:


import { costCalculationApiUrl } from "@/constants/urls"
import { jest } from "@jest/globals"
import { NextRouterMock } from "cypress/mocks/NextRouterMock"
import { GlobalLayoutRouterContext } from "next/dist/shared/lib/app-router-context.shared-runtime"
import { Provider, useSelector } from "react-redux"

import { RootState, store } from "@/redux/store"

import { Analytics } from "../../components"
import { format2APIDate } from "../../helpers"

describe("", () => {
  it("should load data successfully without encountering a 422 error", () => {
    // Mount your component
    cy.mount(
      <Provider store = {store}>
        <Analytics />
      </Provider>
    )
  })
})

Cypress отображает эту проблему:

Эта проблема аналогична этой, но я не использую React Router.

Вы не можете эффективно использовать тесты компонентов для тестирования функций, связанных с маршрутами, при использовании программной навигации, если только вы не попытаетесь смонтировать все приложение, включая маршруты страниц (не знаю, как вы это сделаете).

Boafo 08.03.2024 23:52

Короткий ответ, минимум боли — переключитесь на тест e2e для функций мягкой навигации.

Boafo 08.03.2024 23:52

Спасибо @Boafo, я переключу тест на E2E.

Ala Eddine Menai 09.03.2024 13:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
189
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я решил проблему, обернув компонент SearchPramsContext и указав параметры URL:


import { SearchParamsContext } from "next/dist/shared/lib/hooks-client-context.shared-runtime"
import { Provider } from "react-redux"

import { store } from "@/redux/store"

import { Analytics } from "../../components"

describe("", () => {
  it("", () => {
    const params = new URLSearchParams({ wp: "400" })
    cy.mount(
      <SearchParamsContext.Provider value = {params}>
        <Provider store = {store}>
          <Analytics />
        </Provider>
      </SearchParamsContext.Provider>
    )
  })
})

Другие вопросы по теме