This commit is contained in:
137
frontend/__tests__/components/core/Theme.spec.tsx
Normal file
137
frontend/__tests__/components/core/Theme.spec.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import { render, fireEvent } from "@solidjs/testing-library";
|
||||
import { createSignal } from "solid-js";
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
import { Theme } from "../../../src/ts/components/core/Theme";
|
||||
import { ThemeWithName } from "../../../src/ts/constants/themes";
|
||||
import * as Loader from "../../../src/ts/states/loader-bar";
|
||||
import * as Notifications from "../../../src/ts/states/notifications";
|
||||
import * as ThemeSignal from "../../../src/ts/states/theme";
|
||||
|
||||
vi.mock("../../../src/ts/constants/themes", () => ({
|
||||
themes: {
|
||||
dark: { hasCss: true },
|
||||
light: {},
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("./FavIcon", () => ({
|
||||
FavIcon: () => <div id="favicon" />,
|
||||
}));
|
||||
|
||||
describe("Theme component", () => {
|
||||
const [themeSignal, setThemeSignal] = createSignal<ThemeWithName>({} as any);
|
||||
const themeSignalMock = vi.spyOn(ThemeSignal, "getTheme");
|
||||
const loaderShowMock = vi.spyOn(Loader, "showLoaderBar");
|
||||
const loaderHideMock = vi.spyOn(Loader, "hideLoaderBar");
|
||||
const notificationAddMock = vi.spyOn(Notifications, "showNoticeNotification");
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.clearAllMocks();
|
||||
loaderShowMock.mockClear();
|
||||
loaderHideMock.mockClear();
|
||||
notificationAddMock.mockClear();
|
||||
themeSignalMock.mockImplementation(() => themeSignal());
|
||||
setThemeSignal({
|
||||
name: "dark",
|
||||
bg: "#000",
|
||||
main: "#fff",
|
||||
caret: "#fff",
|
||||
sub: "#aaa",
|
||||
subAlt: "#888",
|
||||
text: "#fff",
|
||||
error: "#f00",
|
||||
errorExtra: "#c00",
|
||||
colorfulError: "#f55",
|
||||
colorfulErrorExtra: "#c55",
|
||||
});
|
||||
});
|
||||
|
||||
it("injects CSS variables based on theme", () => {
|
||||
const { style } = renderComponent();
|
||||
|
||||
expect(style.innerHTML).toEqual(`
|
||||
:root {
|
||||
--bg-color: #000;
|
||||
--main-color: #fff;
|
||||
--caret-color: #fff;
|
||||
--sub-color: #aaa;
|
||||
--sub-alt-color: #888;
|
||||
--text-color: #fff;
|
||||
--error-color: #f00;
|
||||
--error-extra-color: #c00;
|
||||
--colorful-error-color: #f55;
|
||||
--colorful-error-extra-color: #c55;
|
||||
}`);
|
||||
});
|
||||
|
||||
it("updates CSS variables based on signal", () => {
|
||||
setThemeSignal({ name: "light", bg: "#f00" } as any);
|
||||
const { style } = renderComponent();
|
||||
|
||||
expect(style.innerHTML).toContain("--bg-color: #f00;");
|
||||
});
|
||||
|
||||
it("loads CSS file and shows loader when theme has CSS", () => {
|
||||
const { css } = renderComponent();
|
||||
|
||||
expect(css.getAttribute("href")).toBe("/themes/dark.css");
|
||||
expect(loaderShowMock).toHaveBeenCalledOnce();
|
||||
fireEvent.load(css);
|
||||
expect(loaderHideMock).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("removes CSS when theme has no CSS", async () => {
|
||||
themeSignalMock.mockImplementation(() => ({ name: "light" }) as any);
|
||||
const { css } = renderComponent();
|
||||
expect(css.getAttribute("href")).toBe("");
|
||||
});
|
||||
|
||||
it("removes CSS when theme is custom", async () => {
|
||||
themeSignalMock.mockImplementation(() => ({ name: "custom" }) as any);
|
||||
const { css } = renderComponent();
|
||||
expect(css.getAttribute("href")).toBe("");
|
||||
});
|
||||
|
||||
it("handles CSS load error", () => {
|
||||
const { css } = renderComponent();
|
||||
expect(loaderShowMock).toHaveBeenCalledOnce();
|
||||
fireEvent.error(css);
|
||||
expect(loaderHideMock).toHaveBeenCalledOnce();
|
||||
expect(notificationAddMock).toHaveBeenCalledWith("Failed to load theme");
|
||||
});
|
||||
|
||||
it("renders favicon", () => {
|
||||
const { favIcon } = renderComponent();
|
||||
|
||||
expect(favIcon).toBeInTheDocument();
|
||||
expect(favIcon).toBeEmptyDOMElement(); //mocked
|
||||
});
|
||||
|
||||
function renderComponent(): {
|
||||
style: HTMLStyleElement;
|
||||
css: HTMLLinkElement;
|
||||
metaThemeColor: HTMLMetaElement;
|
||||
favIcon: HTMLElement;
|
||||
} {
|
||||
render(() => <Theme />);
|
||||
|
||||
//wait for debounce
|
||||
vi.runAllTimers();
|
||||
|
||||
//make sure content is rendered to the head, not the body
|
||||
const head = document.head;
|
||||
|
||||
return {
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
style: head.querySelector("style#theme")!,
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
css: head.querySelector("link#currentTheme")!,
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
metaThemeColor: head.querySelector("meta#metaThemeColor")!,
|
||||
// oxlint-disable-next-line typescript/no-non-null-assertion
|
||||
favIcon: head.querySelector("#favicon")!,
|
||||
};
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user