Add back keyboard toast tests (#2582)

* Fix global-jsdom initialization

* add back toast tests

* fix keyboard input events.

* add jsdom types
This commit is contained in:
Timo 2024-08-30 15:40:09 +02:00 committed by GitHub
parent e9fc5dadd9
commit 3e57a7692c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 84 additions and 9 deletions

View File

@ -45,6 +45,7 @@
"@testing-library/user-event": "^14.5.1", "@testing-library/user-event": "^14.5.1",
"@types/content-type": "^1.1.5", "@types/content-type": "^1.1.5",
"@types/grecaptcha": "^3.0.9", "@types/grecaptcha": "^3.0.9",
"@types/jsdom": "^21.1.7",
"@types/lodash": "^4.14.199", "@types/lodash": "^4.14.199",
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",

View File

@ -16,6 +16,7 @@ limitations under the License.
import { describe, expect, test, vi } from "vitest"; import { describe, expect, test, vi } from "vitest";
import { render, configure } from "@testing-library/react"; import { render, configure } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Toast } from "../src/Toast"; import { Toast } from "../src/Toast";
import { withFakeTimers } from "./utils/test"; import { withFakeTimers } from "./utils/test";
@ -24,6 +25,9 @@ configure({
defaultHidden: true, defaultHidden: true,
}); });
// Test Explanation:
// This test the toast. We need to use { document: window.document } because the toast listens
// for user input on `window`.
describe("Toast", () => { describe("Toast", () => {
test("renders", () => { test("renders", () => {
const { queryByRole } = render( const { queryByRole } = render(
@ -40,6 +44,33 @@ describe("Toast", () => {
expect(getByRole("dialog")).toMatchSnapshot(); expect(getByRole("dialog")).toMatchSnapshot();
}); });
test("dismisses when Esc is pressed", async () => {
const user = userEvent.setup({ document: window.document });
const onDismiss = vi.fn();
const { debug } = render(
<Toast open={true} onDismiss={onDismiss}>
Hello world!
</Toast>,
);
debug();
await user.keyboard("[Escape]");
expect(onDismiss).toHaveBeenCalled();
});
test("dismisses when background is clicked", async () => {
const user = userEvent.setup();
const onDismiss = vi.fn();
const { getByRole, unmount } = render(
<Toast open={true} onDismiss={onDismiss}>
Hello world!
</Toast>,
);
const background = getByRole("dialog").previousSibling! as Element;
await user.click(background);
expect(onDismiss).toHaveBeenCalled();
unmount();
});
test("dismisses itself after the specified timeout", () => { test("dismisses itself after the specified timeout", () => {
withFakeTimers(() => { withFakeTimers(() => {
const onDismiss = vi.fn(); const onDismiss = vi.fn();

View File

@ -22,6 +22,12 @@ import userEvent from "@testing-library/user-event";
import { useCallViewKeyboardShortcuts } from "../src/useCallViewKeyboardShortcuts"; import { useCallViewKeyboardShortcuts } from "../src/useCallViewKeyboardShortcuts";
// Test Explanation:
// - The main objective is to test `useCallViewKeyboardShortcuts`.
// The TestComponent just wraps a button around that hook.
// - We need to set `userEvent` to the `{document = window.document}` since we are testing the
// `useCallViewKeyboardShortcuts` hook here. Which is listening to window.
interface TestComponentProps { interface TestComponentProps {
setMicrophoneMuted: (muted: boolean) => void; setMicrophoneMuted: (muted: boolean) => void;
onButtonClick?: () => void; onButtonClick?: () => void;
@ -40,24 +46,33 @@ const TestComponent: FC<TestComponentProps> = ({
); );
return ( return (
<div ref={ref}> <div ref={ref}>
<Button onClick={onButtonClick}>I'm a button</Button> <Button onClick={onButtonClick}>TEST</Button>
</div> </div>
); );
}; };
test("spacebar unmutes", async () => { test("spacebar unmutes", async () => {
const user = userEvent.setup(); const user = userEvent.setup({ document: window.document });
let muted = true; let muted = true;
render(<TestComponent setMicrophoneMuted={(m) => (muted = m)} />); render(
<TestComponent
onButtonClick={() => (muted = false)}
setMicrophoneMuted={(m) => {
muted = m;
}}
/>,
);
await user.keyboard("[Space>]"); await user.keyboard("[Space>]");
expect(muted).toBe(false); expect(muted).toBe(false);
await user.keyboard("[/Space]"); await user.keyboard("[/Space]");
expect(muted).toBe(true); expect(muted).toBe(true);
}); });
test("spacebar prioritizes pressing a button", async () => { test("spacebar prioritizes pressing a button", async () => {
const user = userEvent.setup(); const user = userEvent.setup({ document: window.document });
const setMuted = vi.fn(); const setMuted = vi.fn();
const onClick = vi.fn(); const onClick = vi.fn();
render( render(
@ -71,7 +86,7 @@ test("spacebar prioritizes pressing a button", async () => {
}); });
test("unmuting happens in place of the default action", async () => { test("unmuting happens in place of the default action", async () => {
const user = userEvent.setup(); const user = userEvent.setup({ document: window.document });
const defaultPrevented = vi.fn(); const defaultPrevented = vi.fn();
// In the real application, we mostly just want the spacebar shortcut to avoid // In the real application, we mostly just want the spacebar shortcut to avoid
// scrolling the page. But to test that here in JSDOM, we need some kind of // scrolling the page. But to test that here in JSDOM, we need some kind of

View File

@ -13,13 +13,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import "global-jsdom/register"; import "global-jsdom/register";
import globalJsdom from "global-jsdom";
import i18n from "i18next"; import i18n from "i18next";
import posthog from "posthog-js"; import posthog from "posthog-js";
import { initReactI18next } from "react-i18next"; import { initReactI18next } from "react-i18next";
import { afterEach } from "vitest"; import { afterEach, beforeEach } from "vitest";
import { cleanup } from "@testing-library/react"; import { cleanup } from "@testing-library/react";
import { Config } from "./config/Config"; import { Config } from "./config/Config";
@ -36,4 +35,12 @@ i18n.use(initReactI18next).init({
Config.initDefault(); Config.initDefault();
posthog.opt_out_capturing(); posthog.opt_out_capturing();
afterEach(cleanup); // We need to cleanup the global jsDom
// Otherwise we will run into issues with async input test overlapping and throwing.
let cleanupJsDom: { (): void };
beforeEach(() => (cleanupJsDom = globalJsdom()));
afterEach(() => {
cleanupJsDom();
cleanup();
});

View File

@ -2854,6 +2854,15 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA== integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
"@types/jsdom@^21.1.7":
version "21.1.7"
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.7.tgz#9edcb09e0b07ce876e7833922d3274149c898cfa"
integrity sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==
dependencies:
"@types/node" "*"
"@types/tough-cookie" "*"
parse5 "^7.0.0"
"@types/json5@^0.0.29": "@types/json5@^0.0.29":
version "0.0.29" version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@ -2869,6 +2878,13 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
"@types/node@*":
version "22.5.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.1.tgz#de01dce265f6b99ed32b295962045d10b5b99560"
integrity sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==
dependencies:
undici-types "~6.19.2"
"@types/node@>=13.7.0": "@types/node@>=13.7.0":
version "22.1.0" version "22.1.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b"
@ -2940,6 +2956,11 @@
resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.2.tgz#51b1c00b516a5774ada5d611e65eb123f988ef8d" resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.2.tgz#51b1c00b516a5774ada5d611e65eb123f988ef8d"
integrity sha512-MQ1AnmTLOncwEf9IVU+B2e4Hchrku5N67NkgcAHW0p3sdzPe0FNMANxEm6OJUzPniEQGkeT3OROLlCwZJLWFZA== integrity sha512-MQ1AnmTLOncwEf9IVU+B2e4Hchrku5N67NkgcAHW0p3sdzPe0FNMANxEm6OJUzPniEQGkeT3OROLlCwZJLWFZA==
"@types/tough-cookie@*":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==
"@types/uuid@10": "@types/uuid@10":
version "10.0.0" version "10.0.0"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d"