如何在Cypress中模拟NextJS的useSearchParams钩子?

问题描述 投票:0回答:1

我有一个使用 NextJS 中的

useSearchParams
钩子的组件:


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 显示此问题:

这个问题与this类似,但我没有使用React Router。

next.js cypress
1个回答
0
投票

我通过用

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>
    )
  })
})

© www.soinside.com 2019 - 2024. All rights reserved.