Skip to content

Commit 590d2e0

Browse files
Merge pull request #6029 from mozilla/MPP-4184-frontend-test-premium-sub
MPP-4184: test(premium-sub): increasing frontend test coverage
2 parents 07c1620 + 339cef1 commit 590d2e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2442
-491
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from "react";
2+
3+
const Svg = (props: React.SVGProps<SVGSVGElement> & { alt?: string }) => {
4+
const ariaLabel = props["aria-label"] || props.alt;
5+
const { alt, ...svgProps } = props;
6+
return (
7+
<svg data-testid="svg-icon" {...svgProps} aria-label={ariaLabel}>
8+
{ariaLabel && <title>{ariaLabel}</title>}
9+
</svg>
10+
);
11+
};
12+
13+
export const MaskIcon = Svg;
14+
export const PhoneIcon = Svg;
15+
export const VpnIcon = Svg;
16+
17+
export const ChevronLeftIcon = Svg;
18+
export const ChevronRightIcon = Svg;
19+
export const QuotationIcon = Svg;
20+
21+
export const StarIcon = Svg;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react";
2+
3+
type ImgSrc = { src: string } | string;
4+
type ImageProps = { alt?: string; src: ImgSrc; className?: string };
5+
6+
const ImageMock: React.FC<ImageProps> = ({ alt, src, className }) => {
7+
const resolved = typeof src === "string" ? src : src.src;
8+
return (
9+
<img
10+
alt={alt ?? ""}
11+
src={resolved}
12+
className={className}
13+
data-testid="mocked-image"
14+
/>
15+
);
16+
};
17+
18+
export default ImageMock;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// BundleBanner
2+
jest.mock(
3+
"../../src/components/landing/images/bundle-banner-woman-400w.png",
4+
() => ({ __esModule: true, default: "/img400.png" }),
5+
{ virtual: true },
6+
);
7+
jest.mock(
8+
"../../src/components/landing/images/bundle-banner-woman-768w.png",
9+
() => ({ __esModule: true, default: "/img768.png" }),
10+
{ virtual: true },
11+
);
12+
jest.mock(
13+
"../../src/components/landing/images/bundle-float-1.svg",
14+
() => ({ __esModule: true, default: "/float1.svg" }),
15+
{ virtual: true },
16+
);
17+
jest.mock(
18+
"../../src/components/landing/images/bundle-float-2.svg",
19+
() => ({ __esModule: true, default: "/float2.svg" }),
20+
{ virtual: true },
21+
);
22+
jest.mock(
23+
"../../src/components/landing/images/bundle-float-3.svg",
24+
() => ({ __esModule: true, default: "/float3.svg" }),
25+
{ virtual: true },
26+
);
27+
28+
// HighlightedFeatures
29+
jest.mock(
30+
"../../src/components/landing/images/highlighted-features/features-unlimited-email-masks.svg",
31+
() => ({ __esModule: true, default: "/unlimited.svg" }),
32+
{ virtual: true },
33+
);
34+
jest.mock(
35+
"../../src/components/landing/images/highlighted-features/features-instantly-masks-on-the-go.svg",
36+
() => ({ __esModule: true, default: "/on-the-go.svg" }),
37+
{ virtual: true },
38+
);
39+
jest.mock(
40+
"../../src/components/landing/images/highlighted-features/features-reply-to-emails-anon.svg",
41+
() => ({ __esModule: true, default: "/reply.svg" }),
42+
{ virtual: true },
43+
);
44+
jest.mock(
45+
"../../src/components/landing/images/highlighted-features/features-block-promotional-emails.svg",
46+
() => ({ __esModule: true, default: "/block.svg" }),
47+
{ virtual: true },
48+
);
49+
jest.mock(
50+
"../../src/components/landing/images/highlighted-features/features-remove-email-trackers.svg",
51+
() => ({ __esModule: true, default: "/remove.svg" }),
52+
{ virtual: true },
53+
);
54+
55+
// DemoPhone
56+
jest.mock(
57+
"../../src/components/landing/images/hero-image-bg.svg",
58+
() => ({ __esModule: true, default: "/bg.svg" }),
59+
{ virtual: true },
60+
);
61+
jest.mock(
62+
"../../src/components/landing/images/hero-image-premium.svg",
63+
() => ({ __esModule: true, default: "/premium.svg" }),
64+
{ virtual: true },
65+
);
66+
jest.mock(
67+
"../../src/components/landing/images/hero-image-premium-fr.svg",
68+
() => ({ __esModule: true, default: "/premium-fr.svg" }),
69+
{ virtual: true },
70+
);
71+
jest.mock(
72+
"../../src/components/landing/images/hero-image-premium-de.svg",
73+
() => ({ __esModule: true, default: "/premium-de.svg" }),
74+
{ virtual: true },
75+
);
76+
jest.mock(
77+
"../../src/components/landing/images/hero-image-nopremium.svg",
78+
() => ({ __esModule: true, default: "/nopremium.svg" }),
79+
{ virtual: true },
80+
);
81+
jest.mock(
82+
"../../src/components/landing/images/hero-image-fg.svg",
83+
() => ({ __esModule: true, default: "/fg.svg" }),
84+
{ virtual: true },
85+
);
86+
jest.mock(
87+
"../../src/components/landing/images/hero-image-fg-de.svg",
88+
() => ({ __esModule: true, default: "/fg-de.svg" }),
89+
{ virtual: true },
90+
);
91+
jest.mock(
92+
"../../src/components/landing/images/hero-image-fg-fr.svg",
93+
() => ({ __esModule: true, default: "/fg-fr.svg" }),
94+
{ virtual: true },
95+
);
96+
97+
// Reviews
98+
jest.mock(
99+
"../../src/components/landing/images/fx-logo.svg",
100+
() => ({ __esModule: true, default: "/fx-logo.svg" }),
101+
{ virtual: true },
102+
);

frontend/jest.setup.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,158 @@
1+
import React from "react";
12
import "@testing-library/jest-dom";
23
import { toHaveNoViolations } from "jest-axe";
4+
import "./__mocks__/components/landingImages.mocks";
5+
6+
import { mockGetLocaleModule } from "./__mocks__/functions/getLocale";
7+
import { mockUseL10nModule } from "./__mocks__/hooks/l10n";
8+
9+
if (!("IntersectionObserver" in global)) {
10+
class MockIntersectionObserver {
11+
readonly root: Element | null = null;
12+
readonly rootMargin: string = "0px";
13+
readonly thresholds: ReadonlyArray<number> = [0];
14+
15+
constructor(
16+
_cb: IntersectionObserverCallback,
17+
_options?: IntersectionObserverInit,
18+
) {}
19+
20+
observe(): void {}
21+
unobserve(): void {}
22+
disconnect(): void {}
23+
takeRecords(): IntersectionObserverEntry[] {
24+
return [];
25+
}
26+
}
27+
28+
// @ts-expect-error attach to both
29+
global.IntersectionObserver = MockIntersectionObserver;
30+
if (typeof window !== "undefined") {
31+
window.IntersectionObserver = MockIntersectionObserver;
32+
}
33+
}
34+
35+
jest.mock("./src/components/Image", () => ({
36+
__esModule: true,
37+
default: require("./__mocks__/components/ImageMock").default,
38+
}));
39+
40+
jest.mock(require.resolve("./src/components/Icons"), () => {
41+
const Svg = (props: React.SVGProps<SVGSVGElement> & { alt?: string }) => {
42+
const ariaLabel = props["aria-label"] || props.alt;
43+
const { alt, ...svgProps } = props;
44+
return React.createElement(
45+
"svg",
46+
{
47+
"data-testid": "svg-icon",
48+
"aria-label": ariaLabel,
49+
...svgProps,
50+
},
51+
ariaLabel ? React.createElement("title", {}, ariaLabel) : null,
52+
);
53+
};
54+
55+
const moduleObj = new Proxy({ __esModule: true } as Record<string, any>, {
56+
get: (_target, _prop) => Svg,
57+
});
58+
59+
moduleObj.default = new Proxy({}, { get: () => Svg });
60+
61+
return moduleObj;
62+
});
63+
64+
declare global {
65+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
66+
var gaEventMock: jest.Mock<any, any>;
67+
var getLocaleMock: jest.Mock<string, unknown[]>;
68+
var useL10nImpl: () => ReturnType<typeof mockUseL10nModule.useL10n>;
69+
}
70+
71+
beforeEach(() => {
72+
global.gaEventMock = jest.fn();
73+
74+
global.getLocaleMock = mockGetLocaleModule.getLocale as unknown as jest.Mock;
75+
global.getLocaleMock.mockReturnValue("en-GB");
76+
77+
global.useL10nImpl = mockUseL10nModule.useL10n;
78+
});
79+
80+
jest.mock(require.resolve("./src/hooks/gaEvent"), () => ({
81+
__esModule: true,
82+
useGaEvent: () => global.gaEventMock,
83+
}));
84+
85+
jest.mock(require.resolve("./src/hooks/gaViewPing"), () => ({
86+
__esModule: true,
87+
useGaViewPing: () => React.createRef<HTMLAnchorElement>(),
88+
}));
89+
90+
jest.mock(require.resolve("./src/functions/getLocale"), () => ({
91+
__esModule: true,
92+
getLocale: (...args: unknown[]) => global.getLocaleMock(...args),
93+
}));
94+
95+
jest.mock(require.resolve("./src/hooks/l10n"), () => ({
96+
__esModule: true,
97+
useL10n: () => global.useL10nImpl(),
98+
}));
99+
100+
// Next.js router mock
101+
jest.mock(
102+
"next/router",
103+
() => require("./__mocks__/modules/next__router").mockNextRouter,
104+
);
105+
106+
// Next.js Link component mock
107+
jest.mock("next/link", () => {
108+
return {
109+
__esModule: true,
110+
default: ({ href, children, ...props }: any) =>
111+
React.createElement("a", { href, ...props }, children),
112+
};
113+
});
114+
115+
// Google Analytics mock
116+
jest.mock(
117+
"react-ga",
118+
() => require("./__mocks__/modules/react-ga").mockReactGa,
119+
);
120+
121+
// Localized component mock
122+
jest.mock(
123+
require.resolve("./src/components/Localized"),
124+
() => require("./__mocks__/components/Localized").mockLocalizedModule,
125+
);
126+
127+
// Config module mock
128+
jest.mock(
129+
require.resolve("./src/config"),
130+
() => require("./__mocks__/configMock").mockConfigModule,
131+
);
132+
133+
// FxA Flow Tracker mock
134+
jest.mock(
135+
require.resolve("./src/hooks/fxaFlowTracker"),
136+
() => require("./__mocks__/hooks/fxaFlowTracker").mockUseFxaFlowTrackerModule,
137+
);
138+
139+
// React Intersection Observer mock
140+
jest.mock(
141+
"react-intersection-observer",
142+
() =>
143+
require("./__mocks__/modules/react-intersection-observer")
144+
.mockReactIntersectionObsever,
145+
);
146+
147+
// React Toastify mock
148+
jest.mock("react-toastify", () => ({
149+
toast: jest.fn(),
150+
ToastContainer: () => null,
151+
}));
152+
153+
// Waffle flags mock
154+
jest.mock(require.resolve("./src/functions/waffle"), () =>
155+
require("./__mocks__/functions/waffle"),
156+
);
3157

4158
expect.extend(toHaveNoViolations);

frontend/src/components/Banner.test.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
import React from "react";
21
import { render, screen } from "@testing-library/react";
32
import userEvent from "@testing-library/user-event";
43
import Image from "../components/Image";
54
import FirefoxLogo from "./dashboard/images/fx-logo.svg";
65

7-
jest.mock("../hooks/l10n.ts", () => mockUseL10nModule);
8-
9-
import { mockUseL10nModule } from "../../__mocks__/hooks/l10n";
10-
116
import { Banner } from "./Banner";
127

138
describe("<Banner>", () => {

0 commit comments

Comments
 (0)