From 02d99c055734edb3d705044de669f7856c0a8fe7 Mon Sep 17 00:00:00 2001
From: doug-s-nava <92806979+doug-s-nava@users.noreply.github.com>
Date: Mon, 16 Dec 2024 12:18:12 -0500
Subject: [PATCH] [Issue #3078] clean up translation related code (#3089)
* replaces use of "unstable_setRequestLocale" with "setRequestLocale" and passes in dynamic locales
* includes a couple of quality of life improvements related to translation code
* translation should now work if we provide Spanish content
---
frontend/next.config.js | 2 +-
.../src/app/[locale]/[...not-found]/page.tsx | 7 +++--
frontend/src/app/[locale]/layout.tsx | 4 +--
frontend/src/app/[locale]/not-found.tsx | 6 ++--
.../app/[locale]/opportunity/[id]/page.tsx | 11 ++++++--
frontend/src/app/[locale]/page.tsx | 13 +++++----
frontend/src/app/[locale]/process/page.tsx | 13 +++++----
frontend/src/app/[locale]/research/page.tsx | 13 +++++----
frontend/src/app/[locale]/search/layout.tsx | 8 ++++--
frontend/src/app/[locale]/search/page.tsx | 19 ++++++++-----
.../[locale]/subscribe/confirmation/page.tsx | 15 ++++++----
frontend/src/app/[locale]/subscribe/page.tsx | 28 +++++++------------
.../[locale]/subscribe/unsubscribe/page.tsx | 15 ++++++----
frontend/src/components/Layout.tsx | 4 +--
frontend/src/i18n/config.ts | 4 +--
frontend/src/i18n/getMessagesWithFallbacks.ts | 18 +++---------
frontend/src/i18n/{server.ts => request.ts} | 17 ++++++++---
frontend/src/types/intl.ts | 2 ++
.../components/search/SearchFilters.test.tsx | 2 +-
.../components/search/SearchResults.test.tsx | 2 +-
frontend/tests/pages/page.test.tsx | 6 ++--
frontend/tests/pages/process/page.test.tsx | 6 ++--
frontend/tests/pages/research/page.test.tsx | 6 ++--
frontend/tests/pages/search/page.test.tsx | 27 +++++++++++++++---
frontend/tests/pages/subscribe/page.test.tsx | 28 +++++++++++++------
.../subscribe/subscribeEmailAction.test.tsx | 2 +-
frontend/tests/react-utils.tsx | 3 ++
27 files changed, 171 insertions(+), 110 deletions(-)
rename frontend/src/i18n/{server.ts => request.ts} (51%)
diff --git a/frontend/next.config.js b/frontend/next.config.js
index 9a4247e1b..a2dc01e3e 100644
--- a/frontend/next.config.js
+++ b/frontend/next.config.js
@@ -1,6 +1,6 @@
// @ts-check
-const withNextIntl = require("next-intl/plugin")("./src/i18n/server.ts");
+const withNextIntl = require("next-intl/plugin")();
const sassOptions = require("./scripts/sassOptions");
const nrExternals = require("@newrelic/next/load-externals");
diff --git a/frontend/src/app/[locale]/[...not-found]/page.tsx b/frontend/src/app/[locale]/[...not-found]/page.tsx
index 7104ce112..a159ad189 100644
--- a/frontend/src/app/[locale]/[...not-found]/page.tsx
+++ b/frontend/src/app/[locale]/[...not-found]/page.tsx
@@ -1,10 +1,13 @@
import { Metadata } from "next";
+import { LocalizedPageProps } from "src/types/intl";
import { getTranslations } from "next-intl/server";
import { notFound } from "next/navigation";
-export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params: { locale },
+}: LocalizedPageProps) {
+ const t = await getTranslations({ locale });
const meta: Metadata = {
title: t("ErrorPages.page_not_found.title"),
description: t("Index.meta_description"),
diff --git a/frontend/src/app/[locale]/layout.tsx b/frontend/src/app/[locale]/layout.tsx
index 847137623..6c3c779fa 100644
--- a/frontend/src/app/[locale]/layout.tsx
+++ b/frontend/src/app/[locale]/layout.tsx
@@ -12,7 +12,7 @@ import Script from "next/script";
import "src/styles/styles.scss";
import { NextIntlClientProvider } from "next-intl";
-import { getMessages, unstable_setRequestLocale } from "next-intl/server";
+import { getMessages, setRequestLocale } from "next-intl/server";
import Layout from "src/components/Layout";
@@ -61,7 +61,7 @@ export default async function LocaleLayout({ children, params }: Props) {
const { locale } = params;
// Enable static rendering
- unstable_setRequestLocale(locale);
+ setRequestLocale(locale);
// Providing all messages to the client
// side is the easiest way to get started
diff --git a/frontend/src/app/[locale]/not-found.tsx b/frontend/src/app/[locale]/not-found.tsx
index 2498b9845..7831c241e 100644
--- a/frontend/src/app/[locale]/not-found.tsx
+++ b/frontend/src/app/[locale]/not-found.tsx
@@ -1,14 +1,14 @@
import { Metadata } from "next";
import { useTranslations } from "next-intl";
-import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
+import { getTranslations } from "next-intl/server";
import Link from "next/link";
import { GridContainer } from "@trussworks/react-uswds";
import BetaAlert from "src/components/BetaAlert";
export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+ const t = await getTranslations();
const meta: Metadata = {
title: t("ErrorPages.page_not_found.title"),
description: t("Index.meta_description"),
@@ -16,8 +16,8 @@ export async function generateMetadata() {
return meta;
}
+// note that NotFound pages do not take props so cannot be translated
export default function NotFound() {
- unstable_setRequestLocale("en");
const t = useTranslations("ErrorPages.page_not_found");
return (
diff --git a/frontend/src/app/[locale]/opportunity/[id]/page.tsx b/frontend/src/app/[locale]/opportunity/[id]/page.tsx
index 64ebdd551..4ed492676 100644
--- a/frontend/src/app/[locale]/opportunity/[id]/page.tsx
+++ b/frontend/src/app/[locale]/opportunity/[id]/page.tsx
@@ -29,12 +29,17 @@ type OpportunityListingProps = {
export const revalidate = 600; // invalidate ten minutes
export const dynamic = "force-dynamic";
-export async function generateMetadata({ params }: { params: { id: string } }) {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params,
+}: {
+ params: { id: string; locale: string };
+}) {
+ const { id, locale } = params;
+ const t = await getTranslations({ locale });
let title = `${t("OpportunityListing.page_title")}`;
try {
const { data: opportunityData } = await fetchOpportunity({
- subPath: params.id,
+ subPath: id,
});
title = `${t("OpportunityListing.page_title")} - ${opportunityData.opportunity_title}`;
} catch (error) {
diff --git a/frontend/src/app/[locale]/page.tsx b/frontend/src/app/[locale]/page.tsx
index cf9b22804..c722750a1 100644
--- a/frontend/src/app/[locale]/page.tsx
+++ b/frontend/src/app/[locale]/page.tsx
@@ -1,14 +1,17 @@
import { Metadata } from "next";
+import { LocalizedPageProps } from "src/types/intl";
-import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
+import { getTranslations, setRequestLocale } from "next-intl/server";
import BetaAlert from "src/components/BetaAlert";
import IndexGoalContent from "src/components/content/IndexGoalContent";
import ProcessAndResearchContent from "src/components/content/ProcessAndResearchContent";
import Hero from "src/components/Hero";
-export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params: { locale },
+}: LocalizedPageProps) {
+ const t = await getTranslations({ locale });
const meta: Metadata = {
title: t("Index.page_title"),
description: t("Index.meta_description"),
@@ -16,8 +19,8 @@ export async function generateMetadata() {
return meta;
}
-export default function Home() {
- unstable_setRequestLocale("en");
+export default function Home({ params: { locale } }: LocalizedPageProps) {
+ setRequestLocale(locale);
return (
<>
diff --git a/frontend/src/app/[locale]/process/page.tsx b/frontend/src/app/[locale]/process/page.tsx
index 71dee5220..b37862d1c 100644
--- a/frontend/src/app/[locale]/process/page.tsx
+++ b/frontend/src/app/[locale]/process/page.tsx
@@ -3,14 +3,17 @@ import ProcessIntro from "src/app/[locale]/process/ProcessIntro";
import ProcessInvolved from "src/app/[locale]/process/ProcessInvolved";
import ProcessMilestones from "src/app/[locale]/process/ProcessMilestones";
import { PROCESS_CRUMBS } from "src/constants/breadcrumbs";
+import { LocalizedPageProps } from "src/types/intl";
-import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
+import { getTranslations, setRequestLocale } from "next-intl/server";
import BetaAlert from "src/components/BetaAlert";
import Breadcrumbs from "src/components/Breadcrumbs";
-export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params: { locale },
+}: LocalizedPageProps) {
+ const t = await getTranslations({ locale });
const meta: Metadata = {
title: t("Process.page_title"),
description: t("Process.meta_description"),
@@ -18,8 +21,8 @@ export async function generateMetadata() {
return meta;
}
-export default function Process() {
- unstable_setRequestLocale("en");
+export default function Process({ params: { locale } }: LocalizedPageProps) {
+ setRequestLocale(locale);
return (
<>
diff --git a/frontend/src/app/[locale]/research/page.tsx b/frontend/src/app/[locale]/research/page.tsx
index e57c7bd89..92f24c4e9 100644
--- a/frontend/src/app/[locale]/research/page.tsx
+++ b/frontend/src/app/[locale]/research/page.tsx
@@ -5,14 +5,17 @@ import ResearchIntro from "src/app/[locale]/research/ResearchIntro";
import ResearchMethodology from "src/app/[locale]/research/ResearchMethodology";
import ResearchThemes from "src/app/[locale]/research/ResearchThemes";
import { RESEARCH_CRUMBS } from "src/constants/breadcrumbs";
+import { LocalizedPageProps } from "src/types/intl";
-import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
+import { getTranslations, setRequestLocale } from "next-intl/server";
import BetaAlert from "src/components/BetaAlert";
import Breadcrumbs from "src/components/Breadcrumbs";
-export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params: { locale },
+}: LocalizedPageProps) {
+ const t = await getTranslations({ locale });
const meta: Metadata = {
title: t("Research.page_title"),
description: t("Research.meta_description"),
@@ -20,8 +23,8 @@ export async function generateMetadata() {
return meta;
}
-export default function Research() {
- unstable_setRequestLocale("en");
+export default function Research({ params: { locale } }: LocalizedPageProps) {
+ setRequestLocale(locale);
return (
<>
diff --git a/frontend/src/app/[locale]/search/layout.tsx b/frontend/src/app/[locale]/search/layout.tsx
index 0292e9413..74ff132a8 100644
--- a/frontend/src/app/[locale]/search/layout.tsx
+++ b/frontend/src/app/[locale]/search/layout.tsx
@@ -1,6 +1,6 @@
import { SEARCH_CRUMBS } from "src/constants/breadcrumbs";
-import { unstable_setRequestLocale } from "next-intl/server";
+import { setRequestLocale } from "next-intl/server";
import BetaAlert from "src/components/BetaAlert";
import Breadcrumbs from "src/components/Breadcrumbs";
@@ -8,10 +8,14 @@ import SearchCallToAction from "src/components/search/SearchCallToAction";
export default function SearchLayout({
children,
+ params: { locale },
}: {
children: React.ReactNode;
+ params: {
+ locale: string;
+ };
}) {
- unstable_setRequestLocale("en");
+ setRequestLocale(locale);
return (
<>
diff --git a/frontend/src/app/[locale]/search/page.tsx b/frontend/src/app/[locale]/search/page.tsx
index a671436db..d006a905b 100644
--- a/frontend/src/app/[locale]/search/page.tsx
+++ b/frontend/src/app/[locale]/search/page.tsx
@@ -1,12 +1,13 @@
import { Metadata } from "next";
import QueryProvider from "src/app/[locale]/search/QueryProvider";
import withFeatureFlag from "src/hoc/search/withFeatureFlag";
+import { LocalizedPageProps } from "src/types/intl";
import { SearchParamsTypes } from "src/types/search/searchRequestTypes";
import { Breakpoints } from "src/types/uiTypes";
import { convertSearchParamsToProperTypes } from "src/utils/search/convertSearchParamsToProperTypes";
import { useTranslations } from "next-intl";
-import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
+import { getTranslations, setRequestLocale } from "next-intl/server";
import { redirect } from "next/navigation";
import ContentDisplayToggle from "src/components/ContentDisplayToggle";
@@ -15,19 +16,23 @@ import SearchBar from "src/components/search/SearchBar";
import SearchFilters from "src/components/search/SearchFilters";
import SearchResults from "src/components/search/SearchResults";
-export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params: { locale },
+}: LocalizedPageProps) {
+ const t = await getTranslations({ locale });
const meta: Metadata = {
title: t("Search.title"),
description: t("Index.meta_description"),
};
return meta;
}
+type SearchPageProps = {
+ searchParams: SearchParamsTypes;
+ params: { locale: string };
+};
-type SearchPageProps = { searchParams: SearchParamsTypes };
-
-function Search({ searchParams }: SearchPageProps) {
- unstable_setRequestLocale("en");
+function Search({ searchParams, params: { locale } }: SearchPageProps) {
+ setRequestLocale(locale);
const t = useTranslations("Search");
const convertedSearchParams = convertSearchParamsToProperTypes(searchParams);
diff --git a/frontend/src/app/[locale]/subscribe/confirmation/page.tsx b/frontend/src/app/[locale]/subscribe/confirmation/page.tsx
index 2f7178ebf..1b6c7b779 100644
--- a/frontend/src/app/[locale]/subscribe/confirmation/page.tsx
+++ b/frontend/src/app/[locale]/subscribe/confirmation/page.tsx
@@ -1,16 +1,19 @@
import { Metadata } from "next";
import { SUBSCRIBE_CONFIRMATION_CRUMBS } from "src/constants/breadcrumbs";
+import { LocalizedPageProps } from "src/types/intl";
import { useTranslations } from "next-intl";
-import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
+import { getTranslations, setRequestLocale } from "next-intl/server";
import Link from "next/link";
import { Grid, GridContainer } from "@trussworks/react-uswds";
import BetaAlert from "src/components/BetaAlert";
import Breadcrumbs from "src/components/Breadcrumbs";
-export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params: { locale },
+}: LocalizedPageProps) {
+ const t = await getTranslations({ locale });
const meta: Metadata = {
title: t("Subscribe.page_title"),
description: t("Index.meta_description"),
@@ -19,8 +22,10 @@ export async function generateMetadata() {
return meta;
}
-export default function SubscriptionConfirmation() {
- unstable_setRequestLocale("en");
+export default function SubscriptionConfirmation({
+ params: { locale },
+}: LocalizedPageProps) {
+ setRequestLocale(locale);
const t = useTranslations("Subscription_confirmation");
return (
diff --git a/frontend/src/app/[locale]/subscribe/page.tsx b/frontend/src/app/[locale]/subscribe/page.tsx
index 688eed4fe..542a7fcb0 100644
--- a/frontend/src/app/[locale]/subscribe/page.tsx
+++ b/frontend/src/app/[locale]/subscribe/page.tsx
@@ -1,21 +1,19 @@
-import pick from "lodash/pick";
import { Metadata } from "next";
import { SUBSCRIBE_CRUMBS } from "src/constants/breadcrumbs";
+import { LocalizedPageProps } from "src/types/intl";
-import {
- NextIntlClientProvider,
- useMessages,
- useTranslations,
-} from "next-intl";
-import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
+import { useTranslations } from "next-intl";
+import { getTranslations, setRequestLocale } from "next-intl/server";
import { Grid, GridContainer } from "@trussworks/react-uswds";
import BetaAlert from "src/components/BetaAlert";
import Breadcrumbs from "src/components/Breadcrumbs";
import SubscriptionForm from "src/components/subscribe/SubscriptionForm";
-export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params: { locale },
+}: LocalizedPageProps) {
+ const t = await getTranslations({ locale });
const meta: Metadata = {
title: t("Subscribe.page_title"),
description: t("Index.meta_description"),
@@ -24,10 +22,9 @@ export async function generateMetadata() {
return meta;
}
-export default function Subscribe() {
- unstable_setRequestLocale("en");
+export default function Subscribe({ params: { locale } }: LocalizedPageProps) {
+ setRequestLocale(locale);
const t = useTranslations("Subscribe");
- const messages = useMessages();
return (
<>
@@ -54,12 +51,7 @@ export default function Subscribe() {
})}
-
-
-
+
diff --git a/frontend/src/app/[locale]/subscribe/unsubscribe/page.tsx b/frontend/src/app/[locale]/subscribe/unsubscribe/page.tsx
index 9d14df08b..3fd60301e 100644
--- a/frontend/src/app/[locale]/subscribe/unsubscribe/page.tsx
+++ b/frontend/src/app/[locale]/subscribe/unsubscribe/page.tsx
@@ -1,16 +1,19 @@
import { Metadata } from "next";
import { UNSUBSCRIBE_CRUMBS } from "src/constants/breadcrumbs";
+import { LocalizedPageProps } from "src/types/intl";
import { useTranslations } from "next-intl";
-import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
+import { getTranslations, setRequestLocale } from "next-intl/server";
import Link from "next/link";
import { Grid, GridContainer } from "@trussworks/react-uswds";
import BetaAlert from "src/components/BetaAlert";
import Breadcrumbs from "src/components/Breadcrumbs";
-export async function generateMetadata() {
- const t = await getTranslations({ locale: "en" });
+export async function generateMetadata({
+ params: { locale },
+}: LocalizedPageProps) {
+ const t = await getTranslations({ locale });
const meta: Metadata = {
title: t("Subscribe.page_title"),
description: t("Index.meta_description"),
@@ -19,8 +22,10 @@ export async function generateMetadata() {
return meta;
}
-export default function Unsubscribe() {
- unstable_setRequestLocale("en");
+export default function Unsubscribe({
+ params: { locale },
+}: LocalizedPageProps) {
+ setRequestLocale(locale);
const t = useTranslations("Unsubscription_confirmation");
return (
diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx
index 122dbb87b..0c510f49e 100644
--- a/frontend/src/components/Layout.tsx
+++ b/frontend/src/components/Layout.tsx
@@ -5,7 +5,7 @@ import {
useMessages,
useTranslations,
} from "next-intl";
-import { unstable_setRequestLocale } from "next-intl/server";
+import { setRequestLocale } from "next-intl/server";
import Footer from "./Footer";
import GrantsIdentifier from "./GrantsIdentifier";
@@ -17,7 +17,7 @@ type Props = {
};
export default function Layout({ children, locale }: Props) {
- unstable_setRequestLocale(locale);
+ setRequestLocale(locale);
const t = useTranslations();
const messages = useMessages();
diff --git a/frontend/src/i18n/config.ts b/frontend/src/i18n/config.ts
index c7762c889..31eb5f7b2 100644
--- a/frontend/src/i18n/config.ts
+++ b/frontend/src/i18n/config.ts
@@ -11,9 +11,9 @@ type RequestConfig = Awaited<
* List of languages supported by the application. Other tools (Storybook, tests) reference this.
* These must be BCP47 language tags: https://en.wikipedia.org/wiki/IETF_language_tag#List_of_common_primary_language_subtags
*/
-export const locales = ["en", "es"] as const;
+export const locales = ["en", "es"];
export type Locale = (typeof locales)[number];
-export const defaultLocale: Locale = "en";
+export const defaultLocale = "en";
/**
* Specifying a time zone affects the rendering of dates and times.
diff --git a/frontend/src/i18n/getMessagesWithFallbacks.ts b/frontend/src/i18n/getMessagesWithFallbacks.ts
index a9c15c5cf..571f6351c 100644
--- a/frontend/src/i18n/getMessagesWithFallbacks.ts
+++ b/frontend/src/i18n/getMessagesWithFallbacks.ts
@@ -1,5 +1,5 @@
import { merge } from "lodash";
-import { defaultLocale, Locale, locales } from "src/i18n/config";
+import { defaultLocale, Locale } from "src/i18n/config";
interface LocaleFile {
messages: Messages;
@@ -15,21 +15,11 @@ async function importMessages(locale: Locale) {
* from the current locale, the missing key will fallback to the default locale
*/
export async function getMessagesWithFallbacks(
- requestedLocale: string = defaultLocale,
+ requestedLocale: Locale = defaultLocale,
) {
- const isValidLocale = locales.includes(requestedLocale as Locale); // https://github.com/microsoft/TypeScript/issues/26255
- if (!isValidLocale) {
- console.error(
- "Unsupported locale was requested. Falling back to the default locale.",
- { locale: requestedLocale, defaultLocale },
- );
- requestedLocale = defaultLocale;
- }
-
- const targetLocale = requestedLocale as Locale;
- let messages = await importMessages(targetLocale);
+ let messages = await importMessages(requestedLocale);
- if (targetLocale !== defaultLocale) {
+ if (requestedLocale !== defaultLocale) {
const fallbackMessages = await importMessages(defaultLocale);
messages = merge({}, fallbackMessages, messages);
}
diff --git a/frontend/src/i18n/server.ts b/frontend/src/i18n/request.ts
similarity index 51%
rename from frontend/src/i18n/server.ts
rename to frontend/src/i18n/request.ts
index f50df8699..dc62138bb 100644
--- a/frontend/src/i18n/server.ts
+++ b/frontend/src/i18n/request.ts
@@ -1,7 +1,7 @@
-import { getRequestConfig } from "next-intl/server";
+import { defaultLocale, formats, locales, timeZone } from "src/i18n/config";
+import { getMessagesWithFallbacks } from "src/i18n/getMessagesWithFallbacks";
-import { formats, timeZone } from "./config";
-import { getMessagesWithFallbacks } from "./getMessagesWithFallbacks";
+import { getRequestConfig } from "next-intl/server";
/**
* Make locale messages available to all server components.
@@ -10,7 +10,16 @@ import { getMessagesWithFallbacks } from "./getMessagesWithFallbacks";
*/
// @ts-expect-error TS2345: Argument of type error is expected behavior by next-intl maintainer: https://github.com/amannn/next-intl/issues/991#issuecomment-2050087509
-export default getRequestConfig(async ({ locale }) => {
+export default getRequestConfig(async ({ requestLocale }) => {
+ let locale = (await requestLocale) || "";
+
+ const isValidLocale = locales.includes(locale); // https://github.com/microsoft/TypeScript/issues/26255
+ if (!isValidLocale) {
+ console.error(
+ `Unsupported locale (${locale}) was requested. Falling back to the default locale.`,
+ );
+ locale = defaultLocale;
+ }
return {
formats,
messages: await getMessagesWithFallbacks(locale),
diff --git a/frontend/src/types/intl.ts b/frontend/src/types/intl.ts
index 2c512a7bd..ed17560f9 100644
--- a/frontend/src/types/intl.ts
+++ b/frontend/src/types/intl.ts
@@ -4,3 +4,5 @@ import type { getTranslations } from "next-intl/server";
export type TFn =
| ReturnType>
| Awaited>>;
+
+export type LocalizedPageProps = { params: { locale: string } };
diff --git a/frontend/tests/components/search/SearchFilters.test.tsx b/frontend/tests/components/search/SearchFilters.test.tsx
index cc072ccc7..dcfbd0167 100644
--- a/frontend/tests/components/search/SearchFilters.test.tsx
+++ b/frontend/tests/components/search/SearchFilters.test.tsx
@@ -14,7 +14,7 @@ jest.mock("src/hooks/useSearchParamUpdater", () => ({
jest.mock("next-intl/server", () => ({
getTranslations: () => identity,
- unstable_setRequestLocale: identity,
+ setRequestLocale: identity,
}));
jest.mock("next-intl", () => ({
diff --git a/frontend/tests/components/search/SearchResults.test.tsx b/frontend/tests/components/search/SearchResults.test.tsx
index 420ee6227..675d6fec7 100644
--- a/frontend/tests/components/search/SearchResults.test.tsx
+++ b/frontend/tests/components/search/SearchResults.test.tsx
@@ -14,7 +14,7 @@ jest.mock("src/hooks/useSearchParamUpdater", () => ({
jest.mock("next-intl/server", () => ({
getTranslations: () => identity,
- unstable_setRequestLocale: identity,
+ setRequestLocale: identity,
}));
jest.mock("next-intl", () => ({
diff --git a/frontend/tests/pages/page.test.tsx b/frontend/tests/pages/page.test.tsx
index c4e183a1a..4a75d07e7 100644
--- a/frontend/tests/pages/page.test.tsx
+++ b/frontend/tests/pages/page.test.tsx
@@ -6,7 +6,7 @@ import { mockMessages, useTranslationsMock } from "src/utils/testing/intlMocks";
jest.mock("next-intl/server", () => ({
getTranslations: () => identity,
- unstable_setRequestLocale: identity,
+ setRequestLocale: identity,
}));
jest.mock("next-intl", () => ({
@@ -16,7 +16,7 @@ jest.mock("next-intl", () => ({
describe("Home", () => {
it("renders intro text", () => {
- render();
+ render(Home({ params: { locale: "en" } }));
const content = screen.getByText("goal.paragraph_1");
@@ -24,7 +24,7 @@ describe("Home", () => {
});
it("passes accessibility scan", async () => {
- const { container } = render();
+ const { container } = render(Home({ params: { locale: "en" } }));
const results = await waitFor(() => axe(container));
expect(results).toHaveNoViolations();
diff --git a/frontend/tests/pages/process/page.test.tsx b/frontend/tests/pages/process/page.test.tsx
index 6268ac3ff..b0e344e5c 100644
--- a/frontend/tests/pages/process/page.test.tsx
+++ b/frontend/tests/pages/process/page.test.tsx
@@ -6,7 +6,7 @@ import { mockMessages, useTranslationsMock } from "src/utils/testing/intlMocks";
jest.mock("next-intl/server", () => ({
getTranslations: () => identity,
- unstable_setRequestLocale: identity,
+ setRequestLocale: identity,
}));
jest.mock("next-intl", () => ({
@@ -16,7 +16,7 @@ jest.mock("next-intl", () => ({
describe("Process", () => {
it("renders intro text", () => {
- render();
+ render(Process({ params: { locale: "en" } }));
const content = screen.getByText("intro.content");
@@ -24,7 +24,7 @@ describe("Process", () => {
});
it("passes accessibility scan", async () => {
- const { container } = render();
+ const { container } = render(Process({ params: { locale: "en" } }));
const results = await waitFor(() => axe(container));
expect(results).toHaveNoViolations();
diff --git a/frontend/tests/pages/research/page.test.tsx b/frontend/tests/pages/research/page.test.tsx
index 2a0fb3298..4a8daf9c2 100644
--- a/frontend/tests/pages/research/page.test.tsx
+++ b/frontend/tests/pages/research/page.test.tsx
@@ -6,7 +6,7 @@ import { mockMessages, useTranslationsMock } from "src/utils/testing/intlMocks";
jest.mock("next-intl/server", () => ({
getTranslations: () => identity,
- unstable_setRequestLocale: identity,
+ setRequestLocale: identity,
}));
jest.mock("next-intl", () => ({
@@ -16,7 +16,7 @@ jest.mock("next-intl", () => ({
describe("Research", () => {
it("renders intro text", () => {
- render();
+ render(Research({ params: { locale: "en" } }));
const content = screen.getByText("intro.content");
@@ -24,7 +24,7 @@ describe("Research", () => {
});
it("passes accessibility scan", async () => {
- const { container } = render();
+ const { container } = render(Research({ params: { locale: "en" } }));
const results = await waitFor(() => axe(container));
expect(results).toHaveNoViolations();
diff --git a/frontend/tests/pages/search/page.test.tsx b/frontend/tests/pages/search/page.test.tsx
index b54e3e998..62cd9d00e 100644
--- a/frontend/tests/pages/search/page.test.tsx
+++ b/frontend/tests/pages/search/page.test.tsx
@@ -13,7 +13,7 @@ jest.mock("src/hoc/search/withFeatureFlag", () =>
jest.mock("next-intl/server", () => ({
getTranslations: () => identity,
- unstable_setRequestLocale: identity,
+ setRequestLocale: identity,
}));
jest.mock("next-intl", () => ({
@@ -62,6 +62,10 @@ const fetchMock = jest.fn().mockResolvedValue({
status: 200,
});
+// working around the complexities of exporting the component wrapped in a feature flag
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const TypedSearchPageComponent = Search as (props: any) => React.JSX.Element;
+
describe("Search Route", () => {
let originalFetch: typeof global.fetch;
beforeAll(() => {
@@ -78,7 +82,12 @@ describe("Search Route", () => {
status: "forecasted,posted",
};
- render();
+ render(
+ TypedSearchPageComponent({
+ searchParams: mockSearchParams,
+ params: { locale: "en" },
+ }),
+ );
// translation service is mocked, so the expected label here is the translation key rather than the label text
const forecastedCheckbox = await screen.findByLabelText(
@@ -110,7 +119,12 @@ describe("Search Route", () => {
const mockSearchParams = {
status: SEARCH_NO_STATUS_VALUE,
};
- render();
+ render(
+ TypedSearchPageComponent({
+ searchParams: mockSearchParams,
+ params: { locale: "en" },
+ }),
+ );
// None should be checked if the "no status checked" value is present.
const statuses = ["forecasted", "posted", "closed", "archived"];
@@ -127,7 +141,12 @@ describe("Search Route", () => {
const mockSearchParams = {
status: undefined,
};
- render();
+ render(
+ TypedSearchPageComponent({
+ searchParams: mockSearchParams,
+ params: { locale: "en" },
+ }),
+ );
// These should be clicked if no status is present.
const clicked = ["forecasted", "posted"];
diff --git a/frontend/tests/pages/subscribe/page.test.tsx b/frontend/tests/pages/subscribe/page.test.tsx
index 551980338..c1c77e534 100644
--- a/frontend/tests/pages/subscribe/page.test.tsx
+++ b/frontend/tests/pages/subscribe/page.test.tsx
@@ -1,12 +1,13 @@
+import { render, screen, waitFor } from "@testing-library/react";
+import { axe } from "jest-axe";
import { identity } from "lodash";
import Subscribe from "src/app/[locale]/subscribe/page";
-import { render, screen } from "tests/react-utils";
+import { useTranslationsMock } from "src/utils/testing/intlMocks";
jest.mock("react-dom", () => {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const originalModule = jest.requireActual("react-dom");
+ const originalModule =
+ jest.requireActual("react-dom");
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
return {
...originalModule,
useFormStatus: jest.fn(() => ({ pending: false })),
@@ -29,19 +30,28 @@ jest.mock("react-dom", () => {
};
});
+jest.mock("next-intl", () => ({
+ useTranslations: () => useTranslationsMock(),
+}));
+
jest.mock("next-intl/server", () => ({
getTranslations: () => identity,
- unstable_setRequestLocale: identity,
+ setRequestLocale: identity,
}));
describe("Subscribe", () => {
it("renders intro text", () => {
- render();
+ render(Subscribe({ params: { locale: "en" } }));
- const content = screen.getByText(
- "Subscribe to get Simpler.Grants.gov project updates in your inbox!",
- );
+ const content = screen.getByText("intro");
expect(content).toBeInTheDocument();
});
+
+ it("passes accessibility scan", async () => {
+ const { container } = render(Subscribe({ params: { locale: "en" } }));
+ const results = await waitFor(() => axe(container));
+
+ expect(results).toHaveNoViolations();
+ });
});
diff --git a/frontend/tests/pages/subscribe/subscribeEmailAction.test.tsx b/frontend/tests/pages/subscribe/subscribeEmailAction.test.tsx
index a52310dde..a163821b6 100644
--- a/frontend/tests/pages/subscribe/subscribeEmailAction.test.tsx
+++ b/frontend/tests/pages/subscribe/subscribeEmailAction.test.tsx
@@ -12,7 +12,7 @@ afterEach(() => {
jest.mock("next-intl/server", () => ({
getTranslations: () => identity,
- unstable_setRequestLocale: identity,
+ setRequestLocale: identity,
}));
jest.mock("next-intl", () => ({
diff --git a/frontend/tests/react-utils.tsx b/frontend/tests/react-utils.tsx
index c9cf51329..30998ea37 100644
--- a/frontend/tests/react-utils.tsx
+++ b/frontend/tests/react-utils.tsx
@@ -13,6 +13,9 @@ import { NextIntlClientProvider } from "next-intl";
/**
* Wrapper component that provides global context to all tests. Notably,
* it allows our tests to render content when using i18n translation methods.
+ *
+ * Note that this functionality does not work when testing page components, use original
+ * testing methods in that case.
*/
const GlobalProviders = ({ children }: { children: React.ReactNode }) => {
return (