Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test useTheme hook #2625

Open
wants to merge 1 commit into
base: livekit
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@sentry/vite-plugin": "^2.0.0",
"@testing-library/dom": "^10.1.0",
"@testing-library/react": "^16.0.0",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^14.5.1",
"@types/content-type": "^1.1.5",
"@types/grecaptcha": "^3.0.9",
Expand Down
122 changes: 122 additions & 0 deletions src/useTheme.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
Copyright 2024 New Vector Ltd.

SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/

import { renderHook } from "@testing-library/react-hooks";
import {
afterEach,
beforeEach,
describe,
expect,
Mock,
test,
vi,
} from "vitest";

import { useTheme } from "./useTheme";
import { useUrlParams } from "./UrlParams";

// Mock the useUrlParams hook
vi.mock("./UrlParams", () => ({
useUrlParams: vi.fn(),
}));

describe("useTheme", () => {
let originalClassList: DOMTokenList;
beforeEach(() => {
// Save the original classList so we can restore it later
originalClassList = document.body.classList;

vi.spyOn(document.body.classList, "add").mockImplementation(vi.fn());
vi.spyOn(document.body.classList, "remove").mockImplementation(vi.fn());
Comment on lines +33 to +34
Copy link
Member

Choose a reason for hiding this comment

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

Generally .mockImplementation(() => {}) or .mockImplementation(vi.fn()) is a no-op, so these can likely be

Suggested change
vi.spyOn(document.body.classList, "add").mockImplementation(vi.fn());
vi.spyOn(document.body.classList, "remove").mockImplementation(vi.fn());
vi.spyOn(document.body.classList, "add");
vi.spyOn(document.body.classList, "remove");

vi.spyOn(document.body.classList, "item").mockImplementation(() => null);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
vi.spyOn(document.body.classList, "item").mockImplementation(() => null);
vi.spyOn(document.body.classList, "item").mockReturnValue(null);

});

afterEach(() => {
vi.clearAllMocks();
});

test("should apply dark theme by default when no theme is specified", () => {
// Mock useUrlParams to return no theme
(useUrlParams as Mock).mockReturnValue({ theme: null });

renderHook(() => useTheme());

expect(originalClassList.remove).toHaveBeenCalledWith(
"cpd-theme-light",
"cpd-theme-dark",
"cpd-theme-light-hc",
"cpd-theme-dark-hc",
);
expect(originalClassList.add).toHaveBeenCalledWith("cpd-theme-dark");
});

test("should apply light theme when theme is set to light", () => {
// Mock useUrlParams to return light theme
(useUrlParams as Mock).mockReturnValue({ theme: "light" });

renderHook(() => useTheme());

expect(originalClassList.remove).toHaveBeenCalledWith(
"cpd-theme-light",
"cpd-theme-dark",
"cpd-theme-light-hc",
"cpd-theme-dark-hc",
);
expect(originalClassList.add).toHaveBeenCalledWith("cpd-theme-light");
});

test("should apply dark-high-contrast theme when theme is set to dark-high-contrast", () => {
// Mock useUrlParams to return dark-high-contrast theme
(useUrlParams as Mock).mockReturnValue({
theme: "dark-high-contrast",
});

renderHook(() => useTheme());

expect(originalClassList.remove).toHaveBeenCalledWith(
"cpd-theme-light",
"cpd-theme-dark",
"cpd-theme-light-hc",
"cpd-theme-dark-hc",
);
expect(originalClassList.add).toHaveBeenCalledWith("cpd-theme-dark-hc");
});

test("should apply light-high-contrast theme when theme is set to light-high-contrast", () => {
Copy link
Member

Choose a reason for hiding this comment

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

Non-blocking, but this and the above tests are very similar, so test.each could make them a lot shorter

// Mock useUrlParams to return light-high-contrast theme
(useUrlParams as Mock).mockReturnValue({
theme: "light-high-contrast",
});

renderHook(() => useTheme());

expect(originalClassList.remove).toHaveBeenCalledWith(
"cpd-theme-light",
"cpd-theme-dark",
"cpd-theme-light-hc",
"cpd-theme-dark-hc",
);
expect(originalClassList.add).toHaveBeenCalledWith("cpd-theme-light-hc");
});

test("should not reapply the same theme if it hasn't changed", () => {
// Mock useUrlParams to return dark theme initially
(useUrlParams as Mock).mockReturnValue({ theme: "dark" });
// Simulate a previous theme
originalClassList.item = vi.fn().mockReturnValue("cpd-theme-dark");

renderHook(() => useTheme());

expect(document.body.classList.add).not.toHaveBeenCalledWith(
"cpd-theme-dark",
);

// Ensure the 'no-theme' class is removed
expect(document.body.classList.remove).toHaveBeenCalledWith("no-theme");
expect(originalClassList.add).not.toHaveBeenCalled();
});
});
15 changes: 15 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2795,6 +2795,14 @@
lz-string "^1.5.0"
pretty-format "^27.0.2"

"@testing-library/react-hooks@^8.0.1":
version "8.0.1"
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12"
integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==
dependencies:
"@babel/runtime" "^7.12.5"
react-error-boundary "^3.1.0"

"@testing-library/react@^16.0.0":
version "16.0.1"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.0.1.tgz#29c0ee878d672703f5e7579f239005e4e0faa875"
Expand Down Expand Up @@ -6871,6 +6879,13 @@ react-dom@18:
loose-envify "^1.1.0"
scheduler "^0.23.2"

react-error-boundary@^3.1.0:
version "3.1.4"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
dependencies:
"@babel/runtime" "^7.12.5"

react-i18next@^15.0.0:
version "15.0.1"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.0.1.tgz#fc662d93829ecb39683fe2757a47ebfbc5c912a0"
Expand Down