test: add tests for states
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { PANEL_SETTINGS } from "$lib/constants";
|
||||
|
||||
export type ImageConfig = {
|
||||
image: HTMLImageElement | undefined;
|
||||
imageLink: string;
|
||||
@@ -8,8 +10,8 @@ export type ImageConfig = {
|
||||
cropBottom: number;
|
||||
};
|
||||
|
||||
async function createState() {
|
||||
const defaults: ImageConfig = {
|
||||
export class ImageConfigState {
|
||||
#state: ImageConfig = $state({
|
||||
image: undefined,
|
||||
imageLink: "",
|
||||
imageReady: false,
|
||||
@@ -17,70 +19,96 @@ async function createState() {
|
||||
cropTop: 0,
|
||||
cropRight: 0,
|
||||
cropBottom: 0,
|
||||
};
|
||||
});
|
||||
|
||||
let state: ImageConfig = $state({ ...defaults });
|
||||
let currentAbortController: AbortController | null = null;
|
||||
#currentAbortController: AbortController | null = null;
|
||||
|
||||
function cleanup() {
|
||||
if (currentAbortController) {
|
||||
currentAbortController.abort();
|
||||
currentAbortController = null;
|
||||
get image() {
|
||||
return this.#state.image;
|
||||
}
|
||||
get imageReady() {
|
||||
return this.#state.imageReady;
|
||||
}
|
||||
get imageLink() {
|
||||
return this.#state.imageLink;
|
||||
}
|
||||
get cropLeft() {
|
||||
return this.#state.cropLeft;
|
||||
}
|
||||
get cropTop() {
|
||||
return this.#state.cropTop;
|
||||
}
|
||||
get cropRight() {
|
||||
return this.#state.cropRight;
|
||||
}
|
||||
get cropBottom() {
|
||||
return this.#state.cropBottom;
|
||||
}
|
||||
|
||||
set cropLeft(v) {
|
||||
this.#state.cropLeft = v;
|
||||
}
|
||||
set cropTop(v) {
|
||||
this.#state.cropTop = v;
|
||||
}
|
||||
set cropRight(v) {
|
||||
this.#state.cropRight = v;
|
||||
}
|
||||
set cropBottom(v) {
|
||||
this.#state.cropBottom = v;
|
||||
}
|
||||
|
||||
private cleanup() {
|
||||
if (this.#currentAbortController) {
|
||||
this.#currentAbortController.abort();
|
||||
this.#currentAbortController = null;
|
||||
}
|
||||
|
||||
if (state.image) {
|
||||
state.image.onload = null;
|
||||
state.image.onerror = null;
|
||||
state.image.src = "";
|
||||
state.image = undefined;
|
||||
if (this.#state.image) {
|
||||
this.#state.image.onload = null;
|
||||
this.#state.image.onerror = null;
|
||||
this.#state.image.src = "";
|
||||
this.#state.image = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadImageByLink(link: string): Promise<void> {
|
||||
cleanup();
|
||||
async uploadImageByLink(link: string): Promise<void> {
|
||||
this.cleanup();
|
||||
|
||||
state.imageReady = false;
|
||||
state.imageLink = link;
|
||||
this.#state.imageReady = false;
|
||||
this.#state.imageLink = link;
|
||||
|
||||
currentAbortController = new AbortController();
|
||||
const { signal } = currentAbortController;
|
||||
this.#currentAbortController = new AbortController();
|
||||
const { signal } = this.#currentAbortController;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (signal.aborted) {
|
||||
reject(new DOMException("Aborted", "AbortError"));
|
||||
return;
|
||||
}
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = "anonymous";
|
||||
|
||||
img.onload = () => {
|
||||
if (signal.aborted) return;
|
||||
|
||||
const onFinished = () => {
|
||||
img.onload = null;
|
||||
img.onerror = null;
|
||||
};
|
||||
|
||||
state.image = img;
|
||||
state.imageReady = true;
|
||||
img.onload = () => {
|
||||
if (signal.aborted) return;
|
||||
onFinished();
|
||||
this.#state.image = img;
|
||||
this.#state.imageReady = true;
|
||||
resolve();
|
||||
};
|
||||
|
||||
img.onerror = (error) => {
|
||||
img.onerror = () => {
|
||||
if (signal.aborted) return;
|
||||
|
||||
img.onload = null;
|
||||
img.onerror = null;
|
||||
img.src = "";
|
||||
|
||||
state.imageReady = false;
|
||||
onFinished();
|
||||
this.#state.imageReady = false;
|
||||
reject(new Error(`Failed to load image: ${link}`));
|
||||
};
|
||||
|
||||
signal.addEventListener(
|
||||
"abort",
|
||||
() => {
|
||||
img.onload = null;
|
||||
img.onerror = null;
|
||||
onFinished();
|
||||
img.src = "";
|
||||
reject(new DOMException("Aborted", "AbortError"));
|
||||
},
|
||||
@@ -91,30 +119,20 @@ async function createState() {
|
||||
});
|
||||
}
|
||||
|
||||
await uploadImageByLink("./backgrounds/b1.jpg");
|
||||
reset() {
|
||||
this.cleanup();
|
||||
this.#state.imageLink = "";
|
||||
this.#state.imageReady = false;
|
||||
this.#state.cropLeft = 0;
|
||||
this.#state.cropTop = 0;
|
||||
this.#state.cropRight = 0;
|
||||
this.#state.cropBottom = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
get image() {
|
||||
return state.image;
|
||||
},
|
||||
get imageReady() {
|
||||
return state.imageReady;
|
||||
},
|
||||
get imageLink() {
|
||||
return state.imageLink;
|
||||
},
|
||||
|
||||
uploadImageByLink,
|
||||
|
||||
reset() {
|
||||
cleanup();
|
||||
Object.assign(state, defaults);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
cleanup();
|
||||
},
|
||||
};
|
||||
destroy() {
|
||||
this.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
export const imageConfigState = await createState();
|
||||
export const imageConfigState = new ImageConfigState();
|
||||
imageConfigState.uploadImageByLink(PANEL_SETTINGS.DEFAULT_BACKGROUND_IMAGE).catch(() => {});
|
||||
|
||||
@@ -32,7 +32,7 @@ function createState() {
|
||||
return state.fontFamily;
|
||||
},
|
||||
set fontFamily(fontFamily: string) {
|
||||
state.fontFamily = this.fontFamily;
|
||||
state.fontFamily = fontFamily;
|
||||
},
|
||||
get color() {
|
||||
return state.color;
|
||||
|
||||
@@ -7,6 +7,9 @@ function createState() {
|
||||
get theme() {
|
||||
return current;
|
||||
},
|
||||
set theme(value: Theme) {
|
||||
current = value;
|
||||
},
|
||||
toggle() {
|
||||
current = current === "dark" ? "light" : "dark";
|
||||
},
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
// Extend global type
|
||||
declare global {
|
||||
var testUtils: {
|
||||
createMockFile: (name?: string, size?: number, type?: string) => File;
|
||||
createMockPanel: (overrides?: any) => any;
|
||||
waitFor: (ms: number) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
// Mock localStorage for testing
|
||||
const localStorageMock = {
|
||||
getItem: vi.fn(),
|
||||
setItem: vi.fn(),
|
||||
removeItem: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
length: 0,
|
||||
key: vi.fn(),
|
||||
};
|
||||
|
||||
Object.defineProperty(window, "localStorage", {
|
||||
value: localStorageMock,
|
||||
});
|
||||
|
||||
// Mock crypto for testing
|
||||
Object.defineProperty(window, "crypto", {
|
||||
value: {
|
||||
randomUUID: () => "test-uuid-12345",
|
||||
},
|
||||
});
|
||||
|
||||
// Mock FileReader for image upload testing
|
||||
Object.defineProperty(window, "FileReader", {
|
||||
value: vi.fn(() => ({
|
||||
readAsDataURL: vi.fn(),
|
||||
onload: null,
|
||||
onerror: null,
|
||||
result: "data:image/jpeg;base64,/9j/4AAQSkZJRg==",
|
||||
})),
|
||||
});
|
||||
|
||||
// Global test utilities
|
||||
global.testUtils = {
|
||||
createMockFile: (name = "test.jpg", size = 1024, type = "image/jpeg") => {
|
||||
const blob = new Blob([new ArrayBuffer(size)], { type });
|
||||
return new File([blob], name, { type });
|
||||
},
|
||||
|
||||
createMockPanel: (overrides = {}) => ({
|
||||
id: "test-panel-id",
|
||||
backgroundImage: "/backgrounds/b1.jpg",
|
||||
text: {
|
||||
id: "test-text-id",
|
||||
text: "Test Panel",
|
||||
fontSize: 18,
|
||||
fontFamily: "Arial",
|
||||
color: "#ffffff",
|
||||
textAlign: "center" as const,
|
||||
paddingX: 10,
|
||||
verticalOffset: 0,
|
||||
},
|
||||
height: 100,
|
||||
createdAt: new Date("2024-01-01"),
|
||||
updatedAt: new Date("2024-01-01"),
|
||||
...overrides,
|
||||
}),
|
||||
|
||||
waitFor: (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)),
|
||||
};
|
||||
|
||||
// Export cleanup function for use in individual test files
|
||||
export function cleanupMocks() {
|
||||
vi.clearAllMocks();
|
||||
localStorageMock.getItem.mockClear();
|
||||
localStorageMock.setItem.mockClear();
|
||||
localStorageMock.removeItem.mockClear();
|
||||
localStorageMock.clear.mockClear();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
import { PANEL_SETTINGS } from "$lib/constants";
|
||||
import { imageConfigState, ImageConfigState } from "$states/imageConfig.svelte";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let lastOnload: (() => void) | null = null;
|
||||
let lastOnerror: (() => void) | null = null;
|
||||
|
||||
vi.stubGlobal(
|
||||
"Image",
|
||||
class {
|
||||
_onload: (() => void) | null = null;
|
||||
_onerror: (() => void) | null = null;
|
||||
_src: string = "";
|
||||
crossOrigin: string = "";
|
||||
|
||||
set src(val: string) {
|
||||
this._src = val;
|
||||
if (val.includes("error")) {
|
||||
setTimeout(() => this._onerror?.(), 1);
|
||||
} else {
|
||||
setTimeout(() => this._onload?.(), 1);
|
||||
}
|
||||
}
|
||||
get src() {
|
||||
return this._src;
|
||||
}
|
||||
|
||||
set onload(val: any) {
|
||||
this._onload = val;
|
||||
if (val) lastOnload = val;
|
||||
}
|
||||
get onload() {
|
||||
return this._onload;
|
||||
}
|
||||
|
||||
set onerror(val: any) {
|
||||
this._onerror = val;
|
||||
if (val) lastOnerror = val;
|
||||
}
|
||||
get onerror() {
|
||||
return this._onerror;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
describe("ImageConfigState", () => {
|
||||
beforeEach(() => {
|
||||
lastOnload = null;
|
||||
lastOnerror = null;
|
||||
imageConfigState.reset();
|
||||
});
|
||||
|
||||
it("should create new instance with default values", () => {
|
||||
const newState = new ImageConfigState();
|
||||
expect(newState.image).toBeUndefined();
|
||||
expect(newState.imageLink).toBe("");
|
||||
expect(newState.imageReady).toBe(false);
|
||||
expect(newState.cropLeft).toBe(0);
|
||||
expect(newState.cropTop).toBe(0);
|
||||
expect(newState.cropRight).toBe(0);
|
||||
expect(newState.cropBottom).toBe(0);
|
||||
newState.destroy();
|
||||
});
|
||||
|
||||
it("should initialize with default background image", async () => {
|
||||
await imageConfigState.uploadImageByLink(PANEL_SETTINGS.DEFAULT_BACKGROUND_IMAGE);
|
||||
|
||||
expect(imageConfigState.imageReady).toBe(true);
|
||||
expect(imageConfigState.image).toBeDefined();
|
||||
expect(imageConfigState.imageLink).toBe(PANEL_SETTINGS.DEFAULT_BACKGROUND_IMAGE);
|
||||
});
|
||||
|
||||
it("should handle manual image upload correctly", async () => {
|
||||
const testLink = "https://example.com/test.png";
|
||||
const uploadPromise = imageConfigState.uploadImageByLink(testLink);
|
||||
|
||||
expect(imageConfigState.imageReady).toBe(false);
|
||||
|
||||
await uploadPromise;
|
||||
expect(imageConfigState.imageReady).toBe(true);
|
||||
expect(imageConfigState.imageLink).toBe(testLink);
|
||||
});
|
||||
|
||||
it("should reset state to defaults", async () => {
|
||||
await imageConfigState.uploadImageByLink("some-image.png");
|
||||
imageConfigState.cropLeft = 100;
|
||||
|
||||
imageConfigState.reset();
|
||||
|
||||
expect(imageConfigState.imageReady).toBe(false);
|
||||
expect(imageConfigState.imageLink).toBe("");
|
||||
expect(imageConfigState.cropLeft).toBe(0);
|
||||
expect(imageConfigState.image).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle image loading error", async () => {
|
||||
await expect(imageConfigState.uploadImageByLink("error-link")).rejects.toThrow("Failed to load image");
|
||||
|
||||
expect(imageConfigState.imageReady).toBe(false);
|
||||
});
|
||||
|
||||
it("should abort previous upload when new upload starts", async () => {
|
||||
const upload1 = imageConfigState.uploadImageByLink("test1.jpg");
|
||||
const upload2 = imageConfigState.uploadImageByLink("test2.jpg");
|
||||
|
||||
await expect(upload1).rejects.toThrow("Aborted");
|
||||
await expect(upload2).resolves.toBeUndefined();
|
||||
|
||||
expect(imageConfigState.imageLink).toBe("test2.jpg");
|
||||
});
|
||||
|
||||
it("should cleanup previous image before loading new one", async () => {
|
||||
await imageConfigState.uploadImageByLink("test1.jpg");
|
||||
const firstImage = imageConfigState.image;
|
||||
|
||||
await imageConfigState.uploadImageByLink("test2.jpg");
|
||||
|
||||
expect(firstImage?.onload).toBeNull();
|
||||
expect(firstImage?.onerror).toBeNull();
|
||||
});
|
||||
|
||||
it("should set crop values", () => {
|
||||
imageConfigState.cropLeft = 10;
|
||||
imageConfigState.cropTop = 20;
|
||||
imageConfigState.cropRight = 30;
|
||||
imageConfigState.cropBottom = 40;
|
||||
|
||||
expect(imageConfigState.cropLeft).toBe(10);
|
||||
expect(imageConfigState.cropTop).toBe(20);
|
||||
expect(imageConfigState.cropRight).toBe(30);
|
||||
expect(imageConfigState.cropBottom).toBe(40);
|
||||
});
|
||||
|
||||
it("should cleanup image event handlers on reset", async () => {
|
||||
await imageConfigState.uploadImageByLink("test.jpg");
|
||||
const img = imageConfigState.image;
|
||||
|
||||
imageConfigState.reset();
|
||||
|
||||
expect(img?.onload).toBeNull();
|
||||
expect(img?.onerror).toBeNull();
|
||||
});
|
||||
|
||||
it("should abort ongoing upload on reset", async () => {
|
||||
const upload = imageConfigState.uploadImageByLink("test.jpg");
|
||||
|
||||
imageConfigState.reset();
|
||||
|
||||
await expect(upload).rejects.toThrow("Aborted");
|
||||
});
|
||||
|
||||
it("should cleanup resources on destroy", async () => {
|
||||
await imageConfigState.uploadImageByLink("test.jpg");
|
||||
const img = imageConfigState.image;
|
||||
|
||||
imageConfigState.destroy();
|
||||
|
||||
expect(imageConfigState.image).toBeUndefined();
|
||||
expect(img?.onload).toBeNull();
|
||||
expect(img?.onerror).toBeNull();
|
||||
});
|
||||
|
||||
it("should abort ongoing upload on destroy", async () => {
|
||||
const upload = imageConfigState.uploadImageByLink("test.jpg");
|
||||
|
||||
imageConfigState.destroy();
|
||||
|
||||
await expect(upload).rejects.toThrow("Aborted");
|
||||
});
|
||||
|
||||
it("should cover aborted onload branch", async () => {
|
||||
const promise = imageConfigState.uploadImageByLink("test.png");
|
||||
|
||||
imageConfigState.destroy();
|
||||
|
||||
if (lastOnload) lastOnload();
|
||||
|
||||
await expect(promise).rejects.toThrow();
|
||||
expect(imageConfigState.imageReady).toBe(false);
|
||||
});
|
||||
|
||||
it("should cover aborted onerror branch", async () => {
|
||||
const promise = imageConfigState.uploadImageByLink("test.png");
|
||||
|
||||
imageConfigState.destroy();
|
||||
|
||||
if (lastOnerror) lastOnerror();
|
||||
|
||||
await expect(promise).rejects.toThrow();
|
||||
expect(imageConfigState.imageReady).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,263 @@
|
||||
import { describe, expect, it, beforeEach } from "vitest";
|
||||
import { konvaAllStagesState } from "$states/konvaAllStages.svelte";
|
||||
|
||||
describe("konvaAllStages.svelte", () => {
|
||||
beforeEach(() => {
|
||||
// Clear the array before each test
|
||||
konvaAllStagesState.length = 0;
|
||||
});
|
||||
|
||||
describe("initial state", () => {
|
||||
it("should be empty array initially", () => {
|
||||
expect(konvaAllStagesState).toBeDefined();
|
||||
expect(Array.isArray(konvaAllStagesState)).toBe(true);
|
||||
expect(konvaAllStagesState).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should be reactive array", () => {
|
||||
expect(() => {
|
||||
konvaAllStagesState.push({} as any);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("adding stages", () => {
|
||||
it("should add stage to array", () => {
|
||||
const mockStage = { id: "test1" } as any;
|
||||
konvaAllStagesState.push(mockStage);
|
||||
|
||||
expect(konvaAllStagesState).toHaveLength(1);
|
||||
expect(konvaAllStagesState[0]).toStrictEqual(mockStage);
|
||||
});
|
||||
|
||||
it("should add multiple stages", () => {
|
||||
const stages = [
|
||||
{ id: "test1" } as any,
|
||||
{ id: "test2" } as any,
|
||||
{ id: "test3" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
expect(konvaAllStagesState).toHaveLength(3);
|
||||
expect(konvaAllStagesState[0]).toStrictEqual(stages[0]);
|
||||
expect(konvaAllStagesState[1]).toStrictEqual(stages[1]);
|
||||
expect(konvaAllStagesState[2]).toStrictEqual(stages[2]);
|
||||
});
|
||||
|
||||
it("should preserve stage order", () => {
|
||||
const stages = [
|
||||
{ id: "first" } as any,
|
||||
{ id: "second" } as any,
|
||||
{ id: "third" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
expect(konvaAllStagesState[0].id).toBe("first");
|
||||
expect(konvaAllStagesState[1].id).toBe("second");
|
||||
expect(konvaAllStagesState[2].id).toBe("third");
|
||||
});
|
||||
});
|
||||
|
||||
describe("removing stages", () => {
|
||||
it("should remove stage from array", () => {
|
||||
const mockStage = { id: "test1" } as any;
|
||||
konvaAllStagesState.push(mockStage);
|
||||
|
||||
expect(konvaAllStagesState).toHaveLength(1);
|
||||
|
||||
konvaAllStagesState.splice(0, 1);
|
||||
|
||||
expect(konvaAllStagesState).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should remove specific stage", () => {
|
||||
const stages = [
|
||||
{ id: "test1" } as any,
|
||||
{ id: "test2" } as any,
|
||||
{ id: "test3" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
konvaAllStagesState.splice(1, 1); // Remove second stage
|
||||
|
||||
expect(konvaAllStagesState).toHaveLength(2);
|
||||
expect(konvaAllStagesState[0].id).toBe("test1");
|
||||
expect(konvaAllStagesState[1].id).toBe("test3");
|
||||
});
|
||||
|
||||
it("should remove all stages", () => {
|
||||
const stages = [
|
||||
{ id: "test1" } as any,
|
||||
{ id: "test2" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
konvaAllStagesState.length = 0;
|
||||
|
||||
expect(konvaAllStagesState).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updating stages", () => {
|
||||
it("should update stage at index", () => {
|
||||
const mockStage = { id: "test1" } as any;
|
||||
const updatedStage = { id: "updated" } as any;
|
||||
|
||||
konvaAllStagesState.push(mockStage);
|
||||
konvaAllStagesState[0] = updatedStage;
|
||||
|
||||
expect(konvaAllStagesState[0]).toStrictEqual(updatedStage);
|
||||
expect(konvaAllStagesState[0].id).toBe("updated");
|
||||
});
|
||||
|
||||
it("should preserve other stages when updating one", () => {
|
||||
const stages = [
|
||||
{ id: "test1" } as any,
|
||||
{ id: "test2" } as any,
|
||||
{ id: "test3" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
const updatedStage = { id: "updated" } as any;
|
||||
konvaAllStagesState[1] = updatedStage;
|
||||
|
||||
expect(konvaAllStagesState[0]).toStrictEqual(stages[0]);
|
||||
expect(konvaAllStagesState[1]).toStrictEqual(updatedStage);
|
||||
expect(konvaAllStagesState[2]).toStrictEqual(stages[2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("stage properties", () => {
|
||||
it("should preserve stage properties", () => {
|
||||
const mockStage = {
|
||||
id: "test",
|
||||
width: 320,
|
||||
height: 100,
|
||||
attrs: { test: "value" }
|
||||
} as any;
|
||||
|
||||
konvaAllStagesState.push(mockStage);
|
||||
|
||||
expect(konvaAllStagesState[0].id).toBe("test");
|
||||
expect(konvaAllStagesState[0].width).toBe(320);
|
||||
expect(konvaAllStagesState[0].height).toBe(100);
|
||||
expect(konvaAllStagesState[0].attrs).toEqual({ test: "value" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("array methods", () => {
|
||||
it("should support forEach", () => {
|
||||
const stages = [
|
||||
{ id: "test1" } as any,
|
||||
{ id: "test2" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
const visited: string[] = [];
|
||||
konvaAllStagesState.forEach(stage => visited.push(stage.id));
|
||||
|
||||
expect(visited).toEqual(["test1", "test2"]);
|
||||
});
|
||||
|
||||
it("should support map", () => {
|
||||
const stages = [
|
||||
{ id: "test1" } as any,
|
||||
{ id: "test2" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
const ids = konvaAllStagesState.map(stage => stage.id);
|
||||
|
||||
expect(ids).toEqual(["test1", "test2"]);
|
||||
});
|
||||
|
||||
it("should support filter", () => {
|
||||
const stages = [
|
||||
{ id: "test1", type: "panel" } as any,
|
||||
{ id: "test2", type: "preview" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
const panels = konvaAllStagesState.filter(stage => stage.type === "panel");
|
||||
|
||||
expect(panels).toHaveLength(1);
|
||||
expect(panels[0].id).toBe("test1");
|
||||
});
|
||||
|
||||
it("should support find", () => {
|
||||
const stages = [
|
||||
{ id: "test1" } as any,
|
||||
{ id: "test2" } as any,
|
||||
];
|
||||
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
|
||||
const found = konvaAllStagesState.find(stage => stage.id === "test2");
|
||||
|
||||
expect(found).toBeDefined();
|
||||
expect(found?.id).toBe("test2");
|
||||
});
|
||||
|
||||
it("should support length property", () => {
|
||||
expect(konvaAllStagesState.length).toBe(0);
|
||||
|
||||
konvaAllStagesState.push({ id: "test1" } as any);
|
||||
expect(konvaAllStagesState.length).toBe(1);
|
||||
|
||||
konvaAllStagesState.push({ id: "test2" } as any);
|
||||
expect(konvaAllStagesState.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("integration", () => {
|
||||
it("should handle add-remove-add cycle", () => {
|
||||
const stage1 = { id: "test1" } as any;
|
||||
const stage2 = { id: "test2" } as any;
|
||||
|
||||
// Add
|
||||
konvaAllStagesState.push(stage1);
|
||||
expect(konvaAllStagesState).toHaveLength(1);
|
||||
|
||||
// Remove
|
||||
konvaAllStagesState.splice(0, 1);
|
||||
expect(konvaAllStagesState).toHaveLength(0);
|
||||
|
||||
// Add again
|
||||
konvaAllStagesState.push(stage2);
|
||||
expect(konvaAllStagesState).toHaveLength(1);
|
||||
expect(konvaAllStagesState[0]).toStrictEqual(stage2);
|
||||
});
|
||||
|
||||
it("should handle multiple operations", () => {
|
||||
const stages = [
|
||||
{ id: "test1" } as any,
|
||||
{ id: "test2" } as any,
|
||||
{ id: "test3" } as any,
|
||||
];
|
||||
|
||||
// Add all
|
||||
stages.forEach(stage => konvaAllStagesState.push(stage));
|
||||
expect(konvaAllStagesState).toHaveLength(3);
|
||||
|
||||
// Remove middle
|
||||
konvaAllStagesState.splice(1, 1);
|
||||
expect(konvaAllStagesState).toHaveLength(2);
|
||||
|
||||
// Add new at end
|
||||
konvaAllStagesState.push({ id: "test4" } as any);
|
||||
expect(konvaAllStagesState).toHaveLength(3);
|
||||
|
||||
// Update first
|
||||
konvaAllStagesState[0] = { id: "updated" } as any;
|
||||
expect(konvaAllStagesState[0].id).toBe("updated");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { konvaStageState } from "$states/konvaStage.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("konvaStage.svelte", () => {
|
||||
describe("initial state", () => {
|
||||
it("should have undefined stage initially", () => {
|
||||
expect(konvaStageState.stage).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should have stage getter", () => {
|
||||
expect(konvaStageState).toHaveProperty("stage");
|
||||
});
|
||||
|
||||
it("should have stage setter", () => {
|
||||
expect(() => {
|
||||
konvaStageState.stage = {} as any;
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("stage getter", () => {
|
||||
it("should return current stage", () => {
|
||||
const mockStage = { id: "test" } as any;
|
||||
konvaStageState.stage = mockStage;
|
||||
expect(konvaStageState.stage).toBeDefined();
|
||||
expect(konvaStageState.stage).toStrictEqual(mockStage);
|
||||
});
|
||||
|
||||
it("should be undefined only at initialization", () => {
|
||||
expect(konvaStageState.stage).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("stage setter", () => {
|
||||
it("should set stage", () => {
|
||||
const mockStage = { id: "test" } as any;
|
||||
konvaStageState.stage = mockStage;
|
||||
|
||||
expect(konvaStageState.stage).toStrictEqual(mockStage);
|
||||
});
|
||||
|
||||
it("should allow updating stage", () => {
|
||||
const firstStage = { id: "first" } as any;
|
||||
const secondStage = { id: "second" } as any;
|
||||
|
||||
konvaStageState.stage = firstStage;
|
||||
expect(konvaStageState.stage).toStrictEqual(firstStage);
|
||||
|
||||
konvaStageState.stage = secondStage;
|
||||
expect(konvaStageState.stage).toStrictEqual(secondStage);
|
||||
expect(konvaStageState.stage).not.toStrictEqual(firstStage);
|
||||
});
|
||||
});
|
||||
|
||||
// describe("stage lifecycle", () => {
|
||||
// it("should handle stage creation", () => {
|
||||
// const mockStage = { id: "new-stage" } as any;
|
||||
|
||||
// konvaStageState.stage = mockStage;
|
||||
|
||||
// expect(konvaStageState.stage).toStrictEqual(mockStage);
|
||||
// expect(konvaStageState.stage?.id).toBe("new-stage");
|
||||
// });
|
||||
|
||||
// it("should handle stage destruction", () => {
|
||||
// const mockStage = { id: "to-destroy" } as any;
|
||||
|
||||
// konvaStageState.stage = mockStage;
|
||||
// expect(konvaStageState.stage).toBeDefined();
|
||||
|
||||
// konvaStageState.stage = undefined;
|
||||
// expect(konvaStageState.stage).toBeUndefined();
|
||||
// });
|
||||
|
||||
// it("should handle stage replacement", () => {
|
||||
// const oldStage = { id: "old" } as any;
|
||||
// const newStage = { id: "new" } as any;
|
||||
|
||||
// konvaStageState.stage = oldStage;
|
||||
// expect(konvaStageState.stage).toStrictEqual(oldStage);
|
||||
|
||||
// konvaStageState.stage = newStage;
|
||||
// expect(konvaStageState.stage).toStrictEqual(newStage);
|
||||
// expect(konvaStageState.stage).not.toStrictEqual(oldStage);
|
||||
// });
|
||||
// });
|
||||
});
|
||||
@@ -0,0 +1,235 @@
|
||||
import type { HexColor } from "$lib/types";
|
||||
import { textConfigState } from "$states/textConfig.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("textConfig.svelte", () => {
|
||||
describe("initial state", () => {
|
||||
it("should have default fontSize", () => {
|
||||
expect(textConfigState.fontSize).toBe(24);
|
||||
});
|
||||
|
||||
it("should have default fontFamily", () => {
|
||||
expect(textConfigState.fontFamily).toBe("Arial");
|
||||
});
|
||||
|
||||
it("should have default color", () => {
|
||||
expect(textConfigState.color).toBe("#ffffff");
|
||||
});
|
||||
|
||||
it("should have default align", () => {
|
||||
expect(textConfigState.align).toBe("center");
|
||||
});
|
||||
|
||||
it("should have default paddingX", () => {
|
||||
expect(textConfigState.paddingX).toBe(10);
|
||||
});
|
||||
|
||||
it("should have default offsetY", () => {
|
||||
expect(textConfigState.offsetY).toBe(0);
|
||||
});
|
||||
|
||||
it("should have all required properties", () => {
|
||||
expect(textConfigState).toHaveProperty("fontSize");
|
||||
expect(textConfigState).toHaveProperty("fontFamily");
|
||||
expect(textConfigState).toHaveProperty("color");
|
||||
expect(textConfigState).toHaveProperty("align");
|
||||
expect(textConfigState).toHaveProperty("paddingX");
|
||||
expect(textConfigState).toHaveProperty("offsetY");
|
||||
});
|
||||
});
|
||||
|
||||
describe("fontSize", () => {
|
||||
it("should set fontSize", () => {
|
||||
textConfigState.fontSize = 32;
|
||||
expect(textConfigState.fontSize).toBe(32);
|
||||
});
|
||||
|
||||
it("should accept positive values", () => {
|
||||
textConfigState.fontSize = 10;
|
||||
expect(textConfigState.fontSize).toBe(10);
|
||||
|
||||
textConfigState.fontSize = 100;
|
||||
expect(textConfigState.fontSize).toBe(100);
|
||||
});
|
||||
|
||||
it("should accept zero", () => {
|
||||
textConfigState.fontSize = 0;
|
||||
expect(textConfigState.fontSize).toBe(0);
|
||||
});
|
||||
|
||||
it("should accept negative values", () => {
|
||||
textConfigState.fontSize = -10;
|
||||
expect(textConfigState.fontSize).toBe(-10);
|
||||
});
|
||||
|
||||
it("should accept decimal values", () => {
|
||||
textConfigState.fontSize = 16.5;
|
||||
expect(textConfigState.fontSize).toBe(16.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fontFamily", () => {
|
||||
it("should set fontFamily", () => {
|
||||
textConfigState.fontFamily = "Roboto";
|
||||
expect(textConfigState.fontFamily).toBe("Roboto");
|
||||
});
|
||||
|
||||
it("should accept common font families", () => {
|
||||
const fonts = ["Arial", "Helvetica", "Times New Roman", "Georgia", "Verdana"];
|
||||
|
||||
fonts.forEach((font) => {
|
||||
textConfigState.fontFamily = font;
|
||||
expect(textConfigState.fontFamily).toBe(font);
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept empty string", () => {
|
||||
textConfigState.fontFamily = "";
|
||||
expect(textConfigState.fontFamily).toBe("");
|
||||
});
|
||||
|
||||
it("should accept font families with spaces", () => {
|
||||
textConfigState.fontFamily = "Times New Roman";
|
||||
expect(textConfigState.fontFamily).toBe("Times New Roman");
|
||||
});
|
||||
});
|
||||
|
||||
describe("color", () => {
|
||||
it("should set color", () => {
|
||||
textConfigState.color = "#ff0000";
|
||||
expect(textConfigState.color).toBe("#ff0000");
|
||||
});
|
||||
|
||||
it("should accept valid hex colors", () => {
|
||||
const colors: Array<HexColor> = ["#ffffff", "#000000", "#ff0000", "#00ff00", "#0000ff", "#123456", "#abcdef"];
|
||||
|
||||
colors.forEach((color) => {
|
||||
textConfigState.color = color;
|
||||
expect(textConfigState.color).toBe(color);
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept hex colors with uppercase", () => {
|
||||
textConfigState.color = "#FFFFFF";
|
||||
expect(textConfigState.color).toBe("#FFFFFF");
|
||||
});
|
||||
|
||||
it("should accept hex colors with mixed case", () => {
|
||||
textConfigState.color = "#FfFfFf";
|
||||
expect(textConfigState.color).toBe("#FfFfFf");
|
||||
});
|
||||
});
|
||||
|
||||
describe("align", () => {
|
||||
it("should set align", () => {
|
||||
textConfigState.align = "left";
|
||||
expect(textConfigState.align).toBe("left");
|
||||
});
|
||||
|
||||
it("should accept left align", () => {
|
||||
textConfigState.align = "left";
|
||||
expect(textConfigState.align).toBe("left");
|
||||
});
|
||||
|
||||
it("should accept center align", () => {
|
||||
textConfigState.align = "center";
|
||||
expect(textConfigState.align).toBe("center");
|
||||
});
|
||||
|
||||
it("should accept right align", () => {
|
||||
textConfigState.align = "right";
|
||||
expect(textConfigState.align).toBe("right");
|
||||
});
|
||||
});
|
||||
|
||||
describe("paddingX", () => {
|
||||
it("should set paddingX", () => {
|
||||
textConfigState.paddingX = 20;
|
||||
expect(textConfigState.paddingX).toBe(20);
|
||||
});
|
||||
|
||||
it("should accept positive values", () => {
|
||||
textConfigState.paddingX = 10;
|
||||
expect(textConfigState.paddingX).toBe(10);
|
||||
|
||||
textConfigState.paddingX = 100;
|
||||
expect(textConfigState.paddingX).toBe(100);
|
||||
});
|
||||
|
||||
it("should accept zero", () => {
|
||||
textConfigState.paddingX = 0;
|
||||
expect(textConfigState.paddingX).toBe(0);
|
||||
});
|
||||
|
||||
it("should accept negative values", () => {
|
||||
textConfigState.paddingX = -10;
|
||||
expect(textConfigState.paddingX).toBe(-10);
|
||||
});
|
||||
|
||||
it("should accept decimal values", () => {
|
||||
textConfigState.paddingX = 15.5;
|
||||
expect(textConfigState.paddingX).toBe(15.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe("offsetY", () => {
|
||||
it("should set offsetY", () => {
|
||||
textConfigState.offsetY = 20;
|
||||
expect(textConfigState.offsetY).toBe(20);
|
||||
});
|
||||
|
||||
it("should accept positive values", () => {
|
||||
textConfigState.offsetY = 10;
|
||||
expect(textConfigState.offsetY).toBe(10);
|
||||
|
||||
textConfigState.offsetY = 100;
|
||||
expect(textConfigState.offsetY).toBe(100);
|
||||
});
|
||||
|
||||
it("should accept zero", () => {
|
||||
textConfigState.offsetY = 0;
|
||||
expect(textConfigState.offsetY).toBe(0);
|
||||
});
|
||||
|
||||
it("should accept negative values", () => {
|
||||
textConfigState.offsetY = -10;
|
||||
expect(textConfigState.offsetY).toBe(-10);
|
||||
});
|
||||
|
||||
it("should accept decimal values", () => {
|
||||
textConfigState.offsetY = 15.5;
|
||||
expect(textConfigState.offsetY).toBe(15.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe("integration", () => {
|
||||
it("should maintain independent values", () => {
|
||||
textConfigState.fontSize = 32;
|
||||
textConfigState.fontFamily = "Roboto";
|
||||
textConfigState.color = "#ff0000";
|
||||
textConfigState.align = "left";
|
||||
textConfigState.paddingX = 20;
|
||||
textConfigState.offsetY = -10;
|
||||
|
||||
expect(textConfigState.fontSize).toBe(32);
|
||||
expect(textConfigState.fontFamily).toBe("Roboto");
|
||||
expect(textConfigState.color).toBe("#ff0000");
|
||||
expect(textConfigState.align).toBe("left");
|
||||
expect(textConfigState.paddingX).toBe(20);
|
||||
expect(textConfigState.offsetY).toBe(-10);
|
||||
});
|
||||
|
||||
it("should handle multiple updates", () => {
|
||||
const initialFontSize = textConfigState.fontSize;
|
||||
|
||||
textConfigState.fontSize = 32;
|
||||
expect(textConfigState.fontSize).toBe(32);
|
||||
|
||||
textConfigState.fontSize = 48;
|
||||
expect(textConfigState.fontSize).toBe(48);
|
||||
|
||||
textConfigState.fontSize = initialFontSize;
|
||||
expect(textConfigState.fontSize).toBe(initialFontSize);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,202 @@
|
||||
import { textsState } from "$states/texts.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("texts.svelte", () => {
|
||||
describe("initial state", () => {
|
||||
it("should have default texts", () => {
|
||||
expect(textsState.texts).toBeDefined();
|
||||
expect(Array.isArray(textsState.texts)).toBe(true);
|
||||
expect(textsState.texts.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should have correct default texts", () => {
|
||||
const defaultTexts = ["About me", "Links", "Projects"];
|
||||
expect(textsState.texts).toHaveLength(defaultTexts.length);
|
||||
|
||||
textsState.texts.forEach((textItem, index) => {
|
||||
expect(textItem.text).toBe(defaultTexts[index]);
|
||||
expect(textItem.id).toBe(index);
|
||||
});
|
||||
});
|
||||
|
||||
it("should have unique IDs for all texts", () => {
|
||||
const ids = textsState.texts.map((item) => item.id);
|
||||
const uniqueIds = new Set(ids);
|
||||
expect(ids.length).toBe(uniqueIds.size);
|
||||
});
|
||||
});
|
||||
|
||||
describe("addText", () => {
|
||||
it("should add new text", () => {
|
||||
const initialLength = textsState.texts.length;
|
||||
const newText = "Test text";
|
||||
|
||||
textsState.addText(newText);
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength + 1);
|
||||
expect(textsState.texts[initialLength].text).toBe(newText);
|
||||
});
|
||||
|
||||
it("should assign unique ID to new text", () => {
|
||||
const initialIds = textsState.texts.map((item) => item.id);
|
||||
const maxId = Math.max(...initialIds);
|
||||
|
||||
textsState.addText("New text");
|
||||
const newText = textsState.texts[textsState.texts.length - 1];
|
||||
|
||||
expect(newText.id).toBe(maxId + 1);
|
||||
expect(initialIds).not.toContain(newText.id);
|
||||
});
|
||||
|
||||
it("should increment ID counter", () => {
|
||||
const initialLength = textsState.texts.length;
|
||||
const firstNewId = textsState.texts[initialLength - 1].id;
|
||||
|
||||
textsState.addText("First");
|
||||
textsState.addText("Second");
|
||||
|
||||
const firstNew = textsState.texts[initialLength];
|
||||
const secondNew = textsState.texts[initialLength + 1];
|
||||
|
||||
expect(firstNew.id).toBe(firstNewId + 1);
|
||||
expect(secondNew.id).toBe(firstNewId + 2);
|
||||
});
|
||||
|
||||
it("should preserve existing texts when adding new", () => {
|
||||
const initialTexts = [...textsState.texts];
|
||||
const newText = "New text";
|
||||
|
||||
textsState.addText(newText);
|
||||
|
||||
initialTexts.forEach((initialText, index) => {
|
||||
expect(textsState.texts[index]).toStrictEqual(initialText);
|
||||
});
|
||||
});
|
||||
|
||||
it("should add text with empty string", () => {
|
||||
const initialLength = textsState.texts.length;
|
||||
|
||||
textsState.addText("");
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength + 1);
|
||||
expect(textsState.texts[initialLength].text).toBe("");
|
||||
});
|
||||
|
||||
it("should add text with special characters", () => {
|
||||
const specialText = "Test @#$%^&*()_+-={}[]|\\:\";'<>?,./`~";
|
||||
|
||||
textsState.addText(specialText);
|
||||
const addedText = textsState.texts[textsState.texts.length - 1];
|
||||
|
||||
expect(addedText.text).toBe(specialText);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeText", () => {
|
||||
it("should remove text by ID", () => {
|
||||
const initialLength = textsState.texts.length;
|
||||
const idToRemove = textsState.texts[0].id;
|
||||
|
||||
textsState.removeText(idToRemove);
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength - 1);
|
||||
expect(textsState.texts.find((item) => item.id === idToRemove)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not remove text with non-existent ID", () => {
|
||||
const initialLength = textsState.texts.length;
|
||||
const initialTexts = [...textsState.texts];
|
||||
const nonExistentId = 999999;
|
||||
|
||||
textsState.removeText(nonExistentId);
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength);
|
||||
expect(textsState.texts).toStrictEqual(initialTexts);
|
||||
});
|
||||
|
||||
it("should preserve other texts when removing one", () => {
|
||||
const idToRemove = textsState.texts[1].id;
|
||||
const otherTexts = textsState.texts.filter((item) => item.id !== idToRemove);
|
||||
|
||||
textsState.removeText(idToRemove);
|
||||
|
||||
expect(textsState.texts).toStrictEqual(otherTexts);
|
||||
});
|
||||
|
||||
it("should handle removing last text", () => {
|
||||
const lastId = textsState.texts[textsState.texts.length - 1].id;
|
||||
const initialLength = textsState.texts.length;
|
||||
|
||||
textsState.removeText(lastId);
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength - 1);
|
||||
expect(textsState.texts.find((item) => item.id === lastId)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle removing first text", () => {
|
||||
const firstId = textsState.texts[0].id;
|
||||
const initialLength = textsState.texts.length;
|
||||
|
||||
textsState.removeText(firstId);
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength - 1);
|
||||
expect(textsState.texts.find((item) => item.id === firstId)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("texts getter", () => {
|
||||
it("should return array of TextItem", () => {
|
||||
expect(Array.isArray(textsState.texts)).toBe(true);
|
||||
textsState.texts.forEach((item) => {
|
||||
expect(item).toHaveProperty("text");
|
||||
expect(item).toHaveProperty("id");
|
||||
expect(typeof item.text).toBe("string");
|
||||
expect(typeof item.id).toBe("number");
|
||||
});
|
||||
});
|
||||
|
||||
it("should return reactive texts", () => {
|
||||
const initialLength = textsState.texts.length;
|
||||
|
||||
textsState.addText("Test");
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("integration", () => {
|
||||
it("should maintain ID uniqueness after multiple operations", () => {
|
||||
const initialIds = new Set(textsState.texts.map((item) => item.id));
|
||||
|
||||
textsState.addText("First");
|
||||
textsState.addText("Second");
|
||||
textsState.removeText(textsState.texts[0].id);
|
||||
textsState.addText("Third");
|
||||
|
||||
const finalIds = textsState.texts.map((item) => item.id);
|
||||
const uniqueIds = new Set(finalIds);
|
||||
|
||||
expect(finalIds.length).toBe(uniqueIds.size);
|
||||
});
|
||||
|
||||
it("should handle adding and removing multiple texts", () => {
|
||||
const initialLength = textsState.texts.length;
|
||||
const addedIds: number[] = [];
|
||||
|
||||
// Add multiple texts
|
||||
for (let i = 0; i < 5; i++) {
|
||||
textsState.addText(`Text ${i}`);
|
||||
addedIds.push(textsState.texts[textsState.texts.length - 1].id);
|
||||
}
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength + 5);
|
||||
|
||||
// Remove some of them
|
||||
textsState.removeText(addedIds[0]);
|
||||
textsState.removeText(addedIds[2]);
|
||||
textsState.removeText(addedIds[4]);
|
||||
|
||||
expect(textsState.texts).toHaveLength(initialLength + 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { themeState } from "$states/theme.svelte";
|
||||
|
||||
describe("theme.svelte", () => {
|
||||
describe("initial state", () => {
|
||||
it("should have initial theme", () => {
|
||||
expect(themeState.theme).toBeDefined();
|
||||
expect(["light", "dark"]).toContain(themeState.theme);
|
||||
});
|
||||
|
||||
it("should start with dark theme by default", () => {
|
||||
expect(themeState.theme).toBe("dark");
|
||||
});
|
||||
});
|
||||
|
||||
describe("toggle", () => {
|
||||
it("should toggle between light and dark themes", () => {
|
||||
const initialTheme = themeState.theme;
|
||||
|
||||
themeState.toggle();
|
||||
expect(themeState.theme).not.toBe(initialTheme);
|
||||
|
||||
themeState.toggle();
|
||||
expect(themeState.theme).toBe(initialTheme);
|
||||
});
|
||||
|
||||
it("should switch from dark to light", () => {
|
||||
themeState.theme = "dark";
|
||||
themeState.toggle();
|
||||
expect(themeState.theme).toBe("light");
|
||||
});
|
||||
|
||||
it("should switch from light to dark", () => {
|
||||
themeState.theme = "light";
|
||||
themeState.toggle();
|
||||
expect(themeState.theme).toBe("dark");
|
||||
});
|
||||
});
|
||||
|
||||
describe("theme getter", () => {
|
||||
it("should return current theme", () => {
|
||||
themeState.theme = "dark";
|
||||
expect(themeState.theme).toBe("dark");
|
||||
|
||||
themeState.theme = "light";
|
||||
expect(themeState.theme).toBe("light");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user