test(theme): add unit tests

This commit is contained in:
Eric Liu 2025-03-16 13:52:33 -07:00
commit c8fc792851
7 changed files with 243 additions and 24 deletions

View file

@ -1,24 +0,0 @@
<script lang="ts">
import { Theme } from "carbon-components-svelte";
import type { CarbonTheme } from "carbon-components-svelte/Theme/Theme.svelte";
let theme: CarbonTheme = "g10";
</script>
<Theme
bind:theme
persist
persistKey="carbon-theme"
on:update={(e) => console.log(e.detail.theme)}
tokens={{ "button-primary": "violet" }}
render="toggle"
toggle={{
themes: ["g10", "g90"],
labelA: "",
labelB: "",
}}
select={{
themes: ["g10", "g90"],
labelText: "",
}}
/>

View file

@ -0,0 +1,23 @@
<svelte:options accessors />
<script lang="ts">
import { Theme } from "carbon-components-svelte";
import type { CarbonTheme } from "carbon-components-svelte/Theme/Theme.svelte";
export let theme: CarbonTheme = "white";
export let persist = false;
export let tokens = {};
</script>
<div data-testid="theme-wrapper">
<Theme
{theme}
{persist}
{tokens}
on:update={({ detail }) => {
console.log("update", detail);
}}
>
<slot />
</Theme>
</div>

168
tests/Theme/Theme.test.ts Normal file
View file

@ -0,0 +1,168 @@
import { render, screen } from "@testing-library/svelte";
import { tick } from "svelte";
import { user } from "../setup-tests";
import Theme from "./Theme.test.svelte";
import ThemeSelect from "./ThemeSelect.test.svelte";
import ThemeSelectCustom from "./ThemeSelectCustom.test.svelte";
import ThemeToggle from "./ThemeToggle.test.svelte";
import ThemeToggleCustom from "./ThemeToggleCustom.test.svelte";
describe("Theme", () => {
let documentMock: {
setAttribute: ReturnType<typeof vi.spyOn>;
style: { setProperty: ReturnType<typeof vi.spyOn> };
};
let consoleLog: ReturnType<typeof vi.spyOn>;
let localStorageMock: Record<string, string>;
let originalLocalStorage: Storage;
beforeEach(() => {
documentMock = {
setAttribute: vi.spyOn(document.documentElement, "setAttribute"),
style: {
setProperty: vi.spyOn(document.documentElement.style, "setProperty"),
},
};
consoleLog = vi.spyOn(console, "log");
originalLocalStorage = global.localStorage;
localStorageMock = {};
global.localStorage = {
getItem: vi.fn((key) => localStorageMock[key] || null),
setItem: vi.fn((key, value) => {
localStorageMock[key] = value;
}),
removeItem: vi.fn((key) => {
delete localStorageMock[key];
}),
clear: vi.fn(() => {
localStorageMock = {};
}),
length: 0,
key: vi.fn(),
};
});
afterEach(() => {
vi.restoreAllMocks();
global.localStorage = originalLocalStorage;
localStorage.clear();
localStorageMock = {};
});
it("should set default theme to white", () => {
render(Theme);
expect(documentMock.setAttribute).toHaveBeenCalledWith("theme", "white");
});
it("should update theme attribute when theme changes", async () => {
const { component } = render(Theme);
component.$set({ theme: "g100" });
await tick();
expect(documentMock.setAttribute).toHaveBeenCalledWith("theme", "g100");
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g100" });
});
it("should apply custom tokens", async () => {
const tokens = {
"interactive-01": "#ff0000",
"ui-background": "#ffffff",
};
render(Theme, { props: { tokens } });
await tick();
expect(documentMock.style.setProperty).toHaveBeenCalledWith(
"--cds-interactive-01",
"#ff0000",
);
expect(documentMock.style.setProperty).toHaveBeenCalledWith(
"--cds-ui-background",
"#ffffff",
);
});
it("should persist theme in localStorage when persist is true", async () => {
render(Theme, { props: { persist: true } });
await tick();
expect(localStorage.setItem).toHaveBeenCalledWith("theme", "white");
});
it("should load persisted theme from localStorage", async () => {
localStorageMock["theme"] = "g90";
render(Theme, { props: { persist: true } });
await tick();
expect(documentMock.setAttribute).toHaveBeenCalledWith("theme", "g90");
});
it("should warn on invalid theme", async () => {
const consoleWarn = vi.spyOn(console, "warn");
const { component } = render(Theme);
// @ts-ignore - Testing invalid theme
component.$set({ theme: "invalid" });
await tick();
expect(consoleWarn).toHaveBeenCalledWith(
expect.stringContaining('invalid theme "invalid"'),
);
});
it("should render toggle when render prop is set to toggle", async () => {
render(ThemeToggle);
const toggle = screen.getByLabelText("Dark mode");
expect(toggle).toBeInTheDocument();
expect(toggle).toHaveAttribute("type", "checkbox");
await user.click(toggle);
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g100" });
await user.click(toggle);
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "white" });
});
it("should render custom toggle when render prop is set to toggle and custom toggle options are provided", async () => {
render(ThemeToggleCustom);
const toggle = screen.getAllByText("Enable dark mode")[0];
expect(toggle).toBeInTheDocument();
await user.click(toggle);
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g80" });
await user.click(toggle);
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g10" });
});
it("should render select when render prop is set to select", async () => {
render(ThemeSelect);
const select = screen.getByLabelText("Themes");
expect(select).toBeInTheDocument();
await user.selectOptions(select, "g100");
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g100" });
await user.selectOptions(select, "white");
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "white" });
});
it("should render custom select when render prop is set to select and custom select options are provided", async () => {
render(ThemeSelectCustom);
const select = screen.getByLabelText("Select a theme");
expect(select).toBeInTheDocument();
await user.selectOptions(select, "g100");
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g100" });
await user.selectOptions(select, "white");
expect(consoleLog).toHaveBeenCalledWith("update", { theme: "white" });
});
});

View file

@ -0,0 +1,10 @@
<script lang="ts">
import { Theme } from "carbon-components-svelte";
</script>
<Theme
render="select"
on:update={({ detail }) => {
console.log("update", detail);
}}
/>

View file

@ -0,0 +1,15 @@
<script lang="ts">
import { Theme } from "carbon-components-svelte";
</script>
<Theme
render="select"
select={{
themes: ["white", "g90", "g100"],
labelText: "Select a theme",
inline: true,
}}
on:update={({ detail }) => {
console.log("update", detail);
}}
/>

View file

@ -0,0 +1,10 @@
<script lang="ts">
import { Theme } from "carbon-components-svelte";
</script>
<Theme
render="toggle"
on:update={({ detail }) => {
console.log("update", detail);
}}
/>

View file

@ -0,0 +1,17 @@
<script lang="ts">
import { Theme } from "carbon-components-svelte";
</script>
<Theme
render="toggle"
toggle={{
themes: ["g10", "g80"],
labelA: "Enable dark mode",
labelB: "Enable dark mode",
hideLabel: true,
size: "sm",
}}
on:update={({ detail }) => {
console.log("update", detail);
}}
/>