Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ global.IntersectionObserver = jest.fn();
defaultFallbackInView(false);
beforeEach(() => {
setupIntersectionMocking(jest.fn);

// react-dom/server.edge is apparently needed instead of react-dom/server
// to avoid this error:
// > Uncaught ReferenceError: MessageChannel is not defined
// See https://github.com/jsdom/jsdom/issues/2448#issuecomment-1581009331
window.MessageChannel = jest.fn().mockImplementation(() => {
return {
port1: {
postMessage: jest.fn(),
},
port2: {
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
},
};
});
});
afterEach(() => {
resetIntersectionMocking();
Expand Down
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
17 changes: 5 additions & 12 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,20 +168,13 @@ const nextConfig = {
type: "asset/source",
});

config.externals ??= {};
config.externals.push({
knex: "commonjs knex",
});

return config;
},
experimental: {
// Without this setting, Next.js has Webpack trying and failing to load
// uglify-js when compiling MJML email templates to HTML in `renderEmail.ts`:
serverComponentsExternalPackages: ["mjml"],
// Sentry 8.x requires `instrumentation.ts` vs. it's previous custom approach.
instrumentationHook: true,
},
// Without adding MJML here, Next.js has Webpack trying and failing to load
// uglify-js when compiling MJML email templates to HTML in `renderEmail.ts`:
// commonjs and knex are needed to avoid errors about not being able to load
// better-sqlite3, for some reason.
serverExternalPackages: ["mjml", "commonjs", "knex"],
};

const sentryOptions = {
Expand Down
12,716 changes: 6,377 additions & 6,339 deletions package-lock.json

Large diffs are not rendered by default.

32 changes: 18 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,32 +80,32 @@
"@grpc/grpc-js": "1.12.2",
"@leeoniya/ufuzzy": "^1.0.17",
"@mozilla/glean": "^5.0.3",
"@next/third-parties": "^14.2.15",
"@next/third-parties": "15.1.4",
"@sentry/nextjs": "^8.48.0",
"@sentry/node": "^8.0.0",
"@sentry/utils": "^8.48.0",
"@stripe/stripe-js": "^5.5.0",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.10.5",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/react": "19.0.3",
"@types/react-dom": "19.0.2",
"canvas-confetti": "^1.9.3",
"dotenv-flow": "^4.1.0",
"eslint-config-next": "^14.2.15",
"eslint-config-next": "15.1.4",
"ioredis": "^5.4.2",
"jsdom": "^25.0.1",
"jsonwebtoken": "^9.0.2",
"jwk-to-pem": "^2.0.7",
"knex": "^3.1.0",
"mjml": "^4.15.3",
"next": "^14.2.22",
"next": "15.1.4",
"next-auth": "^4.24.11",
"nodemailer": "^6.9.16",
"pg": "^8.13.1",
"react": "^18.3.1",
"react": "19.0.0",
"react-aria": "^3.36.0",
"react-cookie": "^7.2.2",
"react-dom": "^18.3.1",
"react-dom": "19.0.0",
"react-intersection-observer": "^9.15.1",
"react-stately": "^3.34.0",
"react-toastify": "^11.0.2",
Expand All @@ -117,13 +117,13 @@
"@faker-js/faker": "^9.3.0",
"@google-cloud/bigquery": "^7.9.1",
"@playwright/test": "^1.49.1",
"@storybook/addon-a11y": "^8.4.7",
"@storybook/addon-actions": "^8.4.7",
"@storybook/addon-essentials": "^8.4.7",
"@storybook/addon-interactions": "^8.4.7",
"@storybook/addon-links": "^8.4.7",
"@storybook/nextjs": "^8.4.7",
"@storybook/react": "^8.4.6",
"@storybook/addon-a11y": "^8.5.0",
"@storybook/addon-actions": "^8.5.0",
"@storybook/addon-essentials": "^8.5.0",
"@storybook/addon-interactions": "^8.5.0",
"@storybook/addon-links": "^8.5.0",
"@storybook/nextjs": "^8.5.0",
"@storybook/react": "^8.5.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.6.0",
Expand Down Expand Up @@ -166,5 +166,9 @@
"tsx": "^4.19.2",
"typescript": "^5.7.3",
"yaml": "^2.7.0"
},
"overrides": {
"@types/react": "19.0.3",
"@types/react-dom": "19.0.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { renderEmail } from "../../../../../../emails/renderEmail";
import { VerifyEmailAddressEmail } from "../../../../../../emails/templates/verifyEmailAddress/VerifyEmailAddressEmail";
import { sanitizeSubscriberRow } from "../../../../../functions/server/sanitize";
import { getServerSession } from "../../../../../functions/server/getServerSession";
import { getL10n } from "../../../../../functions/l10n/serverComponents";
import {
getAcceptLangHeaderInServerComponents,
getL10n,
} from "../../../../../functions/l10n/serverComponents";
import { getSubscriberByFxaUid } from "../../../../../../db/tables/subscribers";
import { ReactNode } from "react";
import { SubscriberRow } from "knex/types/tables";
Expand Down Expand Up @@ -80,7 +83,7 @@ async function send(
return sendEmail(
emailAddress,
"Test email: " + subject,
renderEmail(template),
await renderEmail(template),
);
}

Expand All @@ -90,7 +93,8 @@ export async function triggerSignupReportEmail(emailAddress: string) {
return false;
}

const l10n = getL10n();
const acceptLangHeader = await getAcceptLangHeaderInServerComponents();
const l10n = getL10n(acceptLangHeader);
const breaches = await getBreachesForEmail(
getSha1(emailAddress),
await getBreaches(),
Expand All @@ -115,7 +119,8 @@ export async function triggerVerificationEmail(emailAddress: string) {
return false;
}

const l10n = getL10n();
const acceptLangHeader = await getAcceptLangHeaderInServerComponents();
const l10n = getL10n(acceptLangHeader);
await send(
emailAddress,
l10n.getString("email-subject-verify"),
Expand All @@ -135,7 +140,8 @@ export async function triggerMonthlyActivityFree(emailAddress: string) {
return false;
}

const l10n = getL10n();
const acceptLangHeader = await getAcceptLangHeaderInServerComponents();
const l10n = getL10n(acceptLangHeader);

if (typeof subscriber.onerep_profile_id === "number") {
await refreshStoredScanResults(subscriber.onerep_profile_id);
Expand All @@ -148,7 +154,7 @@ export async function triggerMonthlyActivityFree(emailAddress: string) {
latestScan.results,
await getSubscriberBreaches({
fxaUid: session.user.subscriber?.fxa_uid,
countryCode: getCountryCode(headers()),
countryCode: getCountryCode(await headers()),
}),
);

Expand All @@ -174,7 +180,8 @@ export async function triggerMonthlyActivityPlus(emailAddress: string) {
return false;
}

const l10n = getL10n();
const acceptLangHeader = await getAcceptLangHeaderInServerComponents();
const l10n = getL10n(acceptLangHeader);

if (typeof subscriber.onerep_profile_id === "number") {
await refreshStoredScanResults(subscriber.onerep_profile_id);
Expand All @@ -187,7 +194,7 @@ export async function triggerMonthlyActivityPlus(emailAddress: string) {
latestScan.results,
await getSubscriberBreaches({
fxaUid: session.user.subscriber?.fxa_uid,
countryCode: getCountryCode(headers()),
countryCode: getCountryCode(await headers()),
}),
);

Expand All @@ -212,7 +219,8 @@ export async function triggerBreachAlert(
return false;
}

const l10n = getL10n();
const acceptLangHeader = await getAcceptLangHeaderInServerComponents();
const l10n = getL10n(acceptLangHeader);

const assumedCountryCode = getSignupLocaleCountry(subscriber);

Expand Down Expand Up @@ -260,7 +268,8 @@ export async function triggerBreachAlert(
}

export async function triggerFirstDataBrokerRemovalFixed(emailAddress: string) {
const l10n = getL10n();
const acceptLangHeader = await getAcceptLangHeaderInServerComponents();
const l10n = getL10n(acceptLangHeader);
const randomScanResult = createRandomScanResult({ status: "removed" });

await send(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import { isAdmin } from "../../../../../api/utils/auth";
import { NoResults, Removals } from "./Removals";
import { getStuckRemovals } from "../../../../../functions/server/getStuckRemovals";

export default async function Page({
searchParams: { page = "1", days = "30", perPage = "100" },
}: {
searchParams: {
export default async function Page(props: {
searchParams: Promise<{
query?: string;
page: string;
days: string;
perPage: string;
};
}>;
}) {
const searchParams = await props.searchParams;

const { page = "1", days = "30", perPage = "100" } = searchParams;

const session = await getServerSession();

if (!isAdmin(session?.user?.email || "")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"use client";

import { useContext, useEffect, useRef, useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import Image from "next/image";
import { Session } from "next-auth";
Expand Down Expand Up @@ -388,7 +389,7 @@ export const View = (props: Props) => {
typeof props.totalNumberOfPerformedScans === "undefined" ||
props.totalNumberOfPerformedScans <
CONST_ONEREP_MAX_SCANS_THRESHOLD ? (
<a
<Link
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flozia This came out of a new linting error, presumably a bugfix in a parser or something. You added the initial <a> - do you remember if that was a conscious decision, or an oversight?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t believe using the a tag here was a conscious decision. The only thing I could imagine is that I (and I’m only guessing) might have encountered an issue with the referrer=dashboard query param. I’ll need to retest the flow, but <Link> not working would be surprising.

ref={waitlistTriggerRef}
href="/user/welcome/free-scan?referrer=dashboard"
onClick={() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,27 @@ import { getExperimentationId } from "../../../../../../../functions/server/getE
import { getElapsedTimeInDaysSinceInitialScan } from "../../../../../../../functions/server/getElapsedTimeInDaysSinceInitialScan";
import { getExperiments } from "../../../../../../../functions/server/getExperiments";
import { getLocale } from "../../../../../../../functions/universal/getLocale";
import { getL10n } from "../../../../../../../functions/l10n/serverComponents";
import {
getAcceptLangHeaderInServerComponents,
getL10n,
} from "../../../../../../../functions/l10n/serverComponents";
import { getDataBrokerRemovalTimeEstimates } from "../../../../../../../functions/server/getDataBrokerRemovalTimeEstimates";

const dashboardTabSlugs = ["action-needed", "fixed"];

type Props = {
params: {
params: Promise<{
slug: string[] | undefined;
};
searchParams: {
}>;
searchParams: Promise<{
nimbus_preview?: string;
dialog?: "subscriptions";
};
}>;
};

export default async function DashboardPage({ params, searchParams }: Props) {
export default async function DashboardPage(props: Props) {
const searchParams = await props.searchParams;
const params = await props.params;
const session = await getServerSession();
if (!checkSession(session) || !session?.user?.subscriber?.id) {
return redirect("/auth/logout");
Expand All @@ -74,7 +79,7 @@ export default async function DashboardPage({ params, searchParams }: Props) {
return redirect(`/user/dashboard/${defaultTab}`);
}

const headersList = headers();
const headersList = await headers();
const countryCode = getCountryCode(headersList);

const profileId = await getOnerepProfileId(session.user.subscriber.id);
Expand Down Expand Up @@ -125,11 +130,11 @@ export default async function DashboardPage({ params, searchParams }: Props) {
});
const userIsEligibleForPremium = isEligibleForPremium(countryCode);

const experimentationId = getExperimentationId(session.user);
const experimentationId = await getExperimentationId(session.user);
const experimentData = await getExperiments({
experimentationId: experimentationId,
countryCode: countryCode,
locale: getLocale(getL10n()),
locale: getLocale(getL10n(await getAcceptLangHeaderInServerComponents())),
previewMode: searchParams.nimbus_preview === "true",
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default async function AutomaticRemovePage() {
email: session.user.email,
});

const countryCode = getCountryCode(headers());
const countryCode = getCountryCode(await headers());
const profileId = await getOnerepProfileId(session.user.subscriber.id);
const scanData = await getScanResultsWithBroker(
profileId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default async function ManualRemovePage() {
email: session.user.email,
});

const countryCode = getCountryCode(headers());
const countryCode = getCountryCode(await headers());
const profileId = await getOnerepProfileId(session.user.subscriber.id);
const scanData = await getScanResultsWithBroker(
profileId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { getEnabledFeatureFlags } from "../../../../../../../../../../db/tables/

export default async function RemovalUnderMaintenance() {
const session = await getServerSession();
const countryCode = getCountryCode(headers());
const countryCode = getCountryCode(await headers());

if (!session?.user?.subscriber?.id) {
return redirect("/");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { hasPremium } from "../../../../../../../../../functions/universal/user"
import { getEnabledFeatureFlags } from "../../../../../../../../../../db/tables/featureFlags";

export default async function StartFreeScanPage() {
const countryCode = getCountryCode(headers());
const countryCode = getCountryCode(await headers());
if (countryCode !== "us") {
redirect("/user/dashboard");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import { StepDeterminationData } from "../../../../../../../../../functions/serv
import { getCountryCode } from "../../../../../../../../../functions/server/getCountryCode";
import { getSubscriberBreaches } from "../../../../../../../../../functions/server/getSubscriberBreaches";
import { getSubscriberEmails } from "../../../../../../../../../functions/server/getSubscriberEmails";
import { getL10n } from "../../../../../../../../../functions/l10n/serverComponents";
import {
getAcceptLangHeaderInServerComponents,
getL10n,
} from "../../../../../../../../../functions/l10n/serverComponents";
import { hasPremium } from "../../../../../../../../../functions/universal/user";
import { getEnabledFeatureFlags } from "../../../../../../../../../../db/tables/featureFlags";

Expand All @@ -23,7 +26,7 @@ export default async function ViewDataBrokers() {
redirect("/user/dashboard");
}

const countryCode = getCountryCode(headers());
const countryCode = getCountryCode(await headers());
const profileId = await getOnerepProfileId(session.user.subscriber.id);
const latestScan = await getScanResultsWithBroker(
profileId,
Expand All @@ -49,7 +52,7 @@ export default async function ViewDataBrokers() {
<ViewDataBrokersView
data={data}
subscriberEmails={subscriberEmails}
l10n={getL10n()}
l10n={getL10n(await getAcceptLangHeaderInServerComponents())}
enabledFeatureFlags={enabledFeatureFlags}
/>
);
Expand Down
Loading
Loading