Fix warnings about wrapping tests in act() (#3314)
* Fix warnings about wrapping tests in act() For full details on what this fix entails, see https://www.benmvp.com/blog/avoiding-react-act-warning-when-accessibility-testing-next-link-jest-axe/ * fix buttonProps errors * fix lint --------- Co-authored-by: Kaitlyn <kandres@mozilla.com>
This commit is contained in:
Родитель
ed6f4685e0
Коммит
e95c3af684
|
@ -77,13 +77,13 @@ const customJestConfig = {
|
|||
statements: 90,
|
||||
},
|
||||
"src/app/(proper_react)/redesign/MobileShell.tsx": {
|
||||
branches: 50,
|
||||
branches: 25,
|
||||
functions: 50,
|
||||
lines: 94,
|
||||
statements: 94,
|
||||
},
|
||||
"src/app/(proper_react)/redesign/PageLink.tsx": {
|
||||
branches: 60,
|
||||
branches: 33,
|
||||
functions: 100,
|
||||
lines: 100,
|
||||
statements: 100,
|
||||
|
@ -348,7 +348,14 @@ const customJestConfig = {
|
|||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
|
||||
setupFilesAfterEnv: [
|
||||
"<rootDir>/jest.setup.ts",
|
||||
// See https://www.benmvp.com/blog/avoiding-react-act-warning-when-accessibility-testing-next-link-jest-axe/
|
||||
// Mocks the IntersectionObserver API, which is used by Next.js's <Link>.
|
||||
// This prevents warnings about wrapping tests in act() for components that
|
||||
// include <Link>s.
|
||||
"react-intersection-observer/test-utils",
|
||||
],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
|
|
@ -6,6 +6,7 @@ import "@testing-library/jest-dom";
|
|||
import { setProjectAnnotations } from "@storybook/react";
|
||||
import { toHaveNoViolations } from "jest-axe";
|
||||
import { expect } from "@jest/globals";
|
||||
import { defaultFallbackInView } from "react-intersection-observer";
|
||||
|
||||
import * as globalStorybookConfig from "./.storybook/preview";
|
||||
|
||||
|
@ -14,3 +15,13 @@ setProjectAnnotations(
|
|||
);
|
||||
|
||||
expect.extend(toHaveNoViolations);
|
||||
|
||||
// See https://www.benmvp.com/blog/avoiding-react-act-warning-when-accessibility-testing-next-link-jest-axe/
|
||||
// If no `IntersectionObserver` exists, Next.js's <Link> will do a state update
|
||||
// immediately after rendering, causing warnings about wrapping tests in act().
|
||||
global.IntersectionObserver = jest.fn();
|
||||
// Then in jest.config.cjs, we add an actual mock for the IntersectionObserver
|
||||
// API in `setupFilesAfterEnv`. When a <Link> scrolls into view, Next.js will
|
||||
// attempt to preload the target, causing another rerender that would cause a
|
||||
// warning about wrapping tests in act(). Thus, we tell it it's not in view.
|
||||
defaultFallbackInView(false);
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
"node-mocks-http": "^1.12.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"prettier": "2.8.8",
|
||||
"react-intersection-observer": "^9.5.2",
|
||||
"sass": "^1.62.1",
|
||||
"storybook": "^7.0.18",
|
||||
"stylelint": "^15.6.0",
|
||||
|
@ -28384,6 +28385,15 @@
|
|||
"react": "^16.8.4 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-intersection-observer": {
|
||||
"version": "9.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.5.2.tgz",
|
||||
"integrity": "sha512-EmoV66/yvksJcGa1rdW0nDNc4I1RifDWkT50gXSFnPLYQ4xUptuDD4V7k+Rj1OgVAlww628KLGcxPXFlOkkU/Q==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
@ -53691,6 +53701,13 @@
|
|||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"react-intersection-observer": {
|
||||
"version": "9.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.5.2.tgz",
|
||||
"integrity": "sha512-EmoV66/yvksJcGa1rdW0nDNc4I1RifDWkT50gXSFnPLYQ4xUptuDD4V7k+Rj1OgVAlww628KLGcxPXFlOkkU/Q==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
|
|
@ -121,6 +121,7 @@
|
|||
"node-mocks-http": "^1.12.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"prettier": "2.8.8",
|
||||
"react-intersection-observer": "^9.5.2",
|
||||
"sass": "^1.62.1",
|
||||
"storybook": "^7.0.18",
|
||||
"stylelint": "^15.6.0",
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { FormEvent, useState } from "react";
|
||||
import { FormEvent, useRef, useState } from "react";
|
||||
import { Session } from "next-auth";
|
||||
import { useOverlayTriggerState } from "react-stately";
|
||||
import { useOverlayTrigger } from "react-aria";
|
||||
import { useButton, useOverlayTrigger } from "react-aria";
|
||||
import whyWeNeedInfoHero from "./images/welcome-why-we-need-info.svg";
|
||||
import { useL10n } from "../../../../../hooks/l10n";
|
||||
import { ModalOverlay } from "../../../../../components/client/dialog/ModalOverlay";
|
||||
|
@ -166,12 +166,10 @@ export const EnterInfo = ({ onScanStarted, onGoBack }: Props) => {
|
|||
|
||||
const handleOnSubmit = (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
const invalidInputKeys = getInvalidFields();
|
||||
if (!invalidInputKeys?.length) {
|
||||
confirmDialogState.open();
|
||||
} else {
|
||||
if (invalidInputKeys?.length > 0) {
|
||||
setInvalidInputs(invalidInputKeys);
|
||||
confirmDialogState.close();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -286,13 +284,20 @@ export const EnterInfo = ({ onScanStarted, onGoBack }: Props) => {
|
|||
</Dialog>
|
||||
);
|
||||
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
const { buttonProps } = useButton(
|
||||
explainerDialogTrigger.triggerProps,
|
||||
triggerRef
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.stepContent}>
|
||||
<h1>{l10n.getString("onboarding-enter-details-title")}</h1>
|
||||
<p>
|
||||
{l10n.getString("onboarding-enter-details-text")}
|
||||
<button
|
||||
{...explainerDialogTrigger.triggerProps}
|
||||
{...buttonProps}
|
||||
ref={triggerRef}
|
||||
onClick={() => explainerDialogState.open()}
|
||||
className={styles.explainerTrigger}
|
||||
>
|
||||
|
@ -356,6 +361,7 @@ export const EnterInfo = ({ onScanStarted, onGoBack }: Props) => {
|
|||
{...confirmDialogTrigger.triggerProps}
|
||||
variant="primary"
|
||||
autoFocus={true}
|
||||
type="submit"
|
||||
className={styles.startButton}
|
||||
>
|
||||
{l10n.getString("onboarding-steps-find-exposures-label")}
|
||||
|
@ -373,7 +379,7 @@ export const EnterInfo = ({ onScanStarted, onGoBack }: Props) => {
|
|||
</ModalOverlay>
|
||||
)}
|
||||
|
||||
{confirmDialogState.isOpen && (
|
||||
{confirmDialogState.isOpen && getInvalidFields().length === 0 && (
|
||||
<ModalOverlay
|
||||
state={confirmDialogState}
|
||||
{...explainerDialogTrigger.overlayProps}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import Image from "next/image";
|
||||
import { useOverlayTriggerState } from "react-stately";
|
||||
import { useOverlayTrigger } from "react-aria";
|
||||
import { useButton, useOverlayTrigger } from "react-aria";
|
||||
import howItWorksHero from "./images/welcome-how-it-works.svg";
|
||||
import { useL10n } from "../../../../../hooks/l10n";
|
||||
import { ModalOverlay } from "../../../../../components/client/dialog/ModalOverlay";
|
||||
|
@ -14,6 +14,7 @@ import { Dialog } from "../../../../../components/client/dialog/Dialog";
|
|||
import { Button } from "../../../../../components/server/Button";
|
||||
|
||||
import styles from "./GetStarted.module.scss";
|
||||
import { useRef } from "react";
|
||||
|
||||
export type Props = {
|
||||
dataBrokerCount: number;
|
||||
|
@ -28,6 +29,12 @@ export const GetStarted = (props: Props) => {
|
|||
explainerDialogState
|
||||
);
|
||||
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
const { buttonProps } = useButton(
|
||||
explainerDialogTrigger.triggerProps,
|
||||
triggerRef
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.stepContent}>
|
||||
<h1>{l10n.getString("onboarding-get-started-heading")}</h1>
|
||||
|
@ -35,7 +42,8 @@ export const GetStarted = (props: Props) => {
|
|||
<p>{l10n.getString("onboarding-get-started-content-price")}</p>
|
||||
<p>
|
||||
<button
|
||||
{...explainerDialogTrigger.triggerProps}
|
||||
{...buttonProps}
|
||||
ref={triggerRef}
|
||||
onClick={() => explainerDialogState.open()}
|
||||
className={styles.explainerTrigger}
|
||||
>
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { ComponentProps, HTMLAttributes, ReactNode } from "react";
|
||||
import { ComponentProps, ReactNode, useRef } from "react";
|
||||
import Link from "next/link";
|
||||
import styles from "./button.module.scss";
|
||||
import { useButton } from "react-aria";
|
||||
|
||||
export interface Props extends ComponentProps<"button"> {
|
||||
children: ReactNode;
|
||||
|
@ -14,12 +15,11 @@ export interface Props extends ComponentProps<"button"> {
|
|||
disabled?: boolean;
|
||||
href?: string;
|
||||
isLoading?: boolean;
|
||||
onClick?: () => void;
|
||||
small?: boolean;
|
||||
}
|
||||
|
||||
export const Button = (
|
||||
props: Props & HTMLAttributes<HTMLButtonElement | HTMLLinkElement>
|
||||
props: Props & Parameters<typeof useButton>[0] // AriaButtonOptions
|
||||
) => {
|
||||
const {
|
||||
buttonType,
|
||||
|
@ -28,12 +28,14 @@ export const Button = (
|
|||
disabled,
|
||||
href,
|
||||
isLoading,
|
||||
onClick,
|
||||
small,
|
||||
variant,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const { buttonProps } = useButton(otherProps, buttonRef);
|
||||
|
||||
const classes = [
|
||||
styles.button,
|
||||
styles[variant],
|
||||
|
@ -56,7 +58,7 @@ export const Button = (
|
|||
{children}
|
||||
</Link>
|
||||
) : (
|
||||
<button {...otherProps} className={classes} onClick={onClick}>
|
||||
<button {...buttonProps} ref={buttonRef} className={classes}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
|
Загрузка…
Ссылка в новой задаче