style: add eslint and prettier, fix style errors
This commit is contained in:
+3
-3
@@ -1,3 +1,3 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import "vitest-canvas-mock";
|
||||
import "web-animations-js";
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import "vitest-canvas-mock";
|
||||
import "web-animations-js";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import CropInline from "$components/image/CropInline.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("CropInline.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
const { container } = render(CropInline);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import CropInline from "$components/image/CropInline.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("CropInline.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
const { container } = render(CropInline);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import ImageManager from "$components/image/ImageManager.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
describe("ImageManager.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(ImageManager);
|
||||
});
|
||||
});
|
||||
import ImageManager from "$components/image/ImageManager.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
describe("ImageManager.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(ImageManager);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import AppHeader from "$components/layout/AppHeader.svelte";
|
||||
import { themeState } from "$states/theme.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("AppHeader.svelte", () => {
|
||||
beforeEach(() => {
|
||||
themeState.current = "dark";
|
||||
});
|
||||
|
||||
it("should toggle theme on button click", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(AppHeader);
|
||||
|
||||
const toggleButton = screen.getByRole("button", { name: /toggle theme/i });
|
||||
|
||||
themeState.current = "dark";
|
||||
await user.click(toggleButton);
|
||||
expect(themeState.current).toBe("light");
|
||||
|
||||
await user.click(toggleButton);
|
||||
expect(themeState.current).toBe("dark");
|
||||
});
|
||||
});
|
||||
import AppHeader from "$components/layout/AppHeader.svelte";
|
||||
import { themeState } from "$states/theme.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("AppHeader.svelte", () => {
|
||||
beforeEach(() => {
|
||||
themeState.current = "dark";
|
||||
});
|
||||
|
||||
it("should toggle theme on button click", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(AppHeader);
|
||||
|
||||
const toggleButton = screen.getByRole("button", { name: /toggle theme/i });
|
||||
|
||||
themeState.current = "dark";
|
||||
await user.click(toggleButton);
|
||||
expect(themeState.current).toBe("light");
|
||||
|
||||
await user.click(toggleButton);
|
||||
expect(themeState.current).toBe("dark");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import CardTest from "./CardTest.svelte";
|
||||
|
||||
describe("Card.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(CardTest);
|
||||
expect(screen.getByText("Test Title")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("card-content")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import CardTest from "./CardTest.svelte";
|
||||
|
||||
describe("Card.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(CardTest);
|
||||
expect(screen.getByText("Test Title")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("card-content")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import Card from "$components/layout/Card.svelte";
|
||||
</script>
|
||||
|
||||
<Card title="Test Title">
|
||||
<div data-testid="card-content">Test content</div>
|
||||
</Card>
|
||||
<script>
|
||||
import Card from "$components/layout/Card.svelte";
|
||||
</script>
|
||||
|
||||
<Card title="Test Title">
|
||||
<div data-testid="card-content">Test content</div>
|
||||
</Card>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import InputGroupTest from "./InputGroupTest.svelte";
|
||||
|
||||
describe("InputGroup.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(InputGroupTest);
|
||||
expect(screen.getByTestId("input1")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("input2")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("button")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import InputGroupTest from "./InputGroupTest.svelte";
|
||||
|
||||
describe("InputGroup.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(InputGroupTest);
|
||||
expect(screen.getByTestId("input1")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("input2")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("button")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script>
|
||||
import InputGroup from "$components/layout/InputGroup.svelte";
|
||||
</script>
|
||||
|
||||
<InputGroup>
|
||||
<input type="text" data-testid="input1" />
|
||||
<input type="text" data-testid="input2" />
|
||||
<button data-testid="button">Кнопка</button>
|
||||
</InputGroup>
|
||||
<script>
|
||||
import InputGroup from "$components/layout/InputGroup.svelte";
|
||||
</script>
|
||||
|
||||
<InputGroup>
|
||||
<input type="text" data-testid="input1" />
|
||||
<input type="text" data-testid="input2" />
|
||||
<button data-testid="button">Кнопка</button>
|
||||
</InputGroup>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import PanelBar from "$components/layout/PanelBar.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("PanelBar.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
const { container } = render(PanelBar);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import PanelBar from "$components/layout/PanelBar.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("PanelBar.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
const { container } = render(PanelBar);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import SettingsGridTest from "./SettingsGridTest.svelte";
|
||||
|
||||
describe("SettingsGrid.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(SettingsGridTest);
|
||||
expect(screen.getByTestId("item1")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("item2")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("item3")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import SettingsGridTest from "./SettingsGridTest.svelte";
|
||||
|
||||
describe("SettingsGrid.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(SettingsGridTest);
|
||||
expect(screen.getByTestId("item1")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("item2")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("item3")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script>
|
||||
import SettingsGrid from "$components/layout/SettingsGrid.svelte";
|
||||
</script>
|
||||
|
||||
<SettingsGrid>
|
||||
<div data-testid="item1">Item 1</div>
|
||||
<div data-testid="item2">Item 2</div>
|
||||
<div data-testid="item3">Item 3</div>
|
||||
</SettingsGrid>
|
||||
<script>
|
||||
import SettingsGrid from "$components/layout/SettingsGrid.svelte";
|
||||
</script>
|
||||
|
||||
<SettingsGrid>
|
||||
<div data-testid="item1">Item 1</div>
|
||||
<div data-testid="item2">Item 2</div>
|
||||
<div data-testid="item3">Item 3</div>
|
||||
</SettingsGrid>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import SettingsRowTest from "./SettingsRowTest.svelte";
|
||||
|
||||
describe("SettingsRow.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(SettingsRowTest);
|
||||
expect(screen.getByText("Test Label")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("test-input")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import SettingsRowTest from "./SettingsRowTest.svelte";
|
||||
|
||||
describe("SettingsRow.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(SettingsRowTest);
|
||||
expect(screen.getByText("Test Label")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("test-input")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import SettingsRow from "$components/layout/SettingsRow.svelte";
|
||||
</script>
|
||||
|
||||
<SettingsRow label="Test Label">
|
||||
<input type="text" data-testid="test-input" />
|
||||
</SettingsRow>
|
||||
<script>
|
||||
import SettingsRow from "$components/layout/SettingsRow.svelte";
|
||||
</script>
|
||||
|
||||
<SettingsRow label="Test Label">
|
||||
<input type="text" data-testid="test-input" />
|
||||
</SettingsRow>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import TextBar from "$components/layout/TextBar.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("TextBar.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
const { container } = render(TextBar);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import TextBar from "$components/layout/TextBar.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("TextBar.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
const { container } = render(TextBar);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import Preview from "$components/panel/Preview.svelte";
|
||||
import { cleanup, render } from "@testing-library/svelte";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe("Preview.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
const { container } = render(Preview, {
|
||||
props: {
|
||||
text: "Test text",
|
||||
stage: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should have stage element", () => {
|
||||
const { container } = render(Preview, {
|
||||
props: {
|
||||
text: "Test",
|
||||
stage: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const stage = container.querySelector("canvas");
|
||||
expect(stage).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import Preview from "$components/panel/Preview.svelte";
|
||||
import { cleanup, render } from "@testing-library/svelte";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe("Preview.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
const { container } = render(Preview, {
|
||||
props: {
|
||||
text: "Test text",
|
||||
stage: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should have stage element", () => {
|
||||
const { container } = render(Preview, {
|
||||
props: {
|
||||
text: "Test",
|
||||
stage: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const stage = container.querySelector("canvas");
|
||||
expect(stage).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import PreviewAll from "$components/panel/PreviewAll.svelte";
|
||||
import { textsState } from "$states/texts.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { beforeEach, describe, it } from "vitest";
|
||||
|
||||
describe("PreviewAll.svelte", () => {
|
||||
beforeEach(() => {
|
||||
textsState.texts.length = 0;
|
||||
});
|
||||
|
||||
it("should render without crashing", () => {
|
||||
render(PreviewAll);
|
||||
});
|
||||
});
|
||||
import PreviewAll from "$components/panel/PreviewAll.svelte";
|
||||
import { textsState } from "$states/texts.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { beforeEach, describe, it } from "vitest";
|
||||
|
||||
describe("PreviewAll.svelte", () => {
|
||||
beforeEach(() => {
|
||||
textsState.texts.length = 0;
|
||||
});
|
||||
|
||||
it("should render without crashing", () => {
|
||||
render(PreviewAll);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import { SlideDirection } from "$lib/constants";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PreviewControlsTest from "./PreviewControlsTest.svelte";
|
||||
|
||||
describe("PreviewControls logic", () => {
|
||||
it("should increment current and update direction on next click", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(PreviewControlsTest, { props: { current: 0, max: 3 } });
|
||||
|
||||
const nextBtn = screen.getByRole("button", { name: /next slide/i });
|
||||
|
||||
await user.click(nextBtn);
|
||||
|
||||
expect(screen.getByTestId("current").textContent).toBe("1");
|
||||
expect(screen.getByTestId("direction").textContent).toBe(SlideDirection.NEXT);
|
||||
});
|
||||
|
||||
it("should decrement current on prev click", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(PreviewControlsTest, { props: { current: 2, max: 3 } });
|
||||
|
||||
const prevBtn = screen.getByRole("button", { name: /previous slide/i });
|
||||
|
||||
await user.click(prevBtn);
|
||||
|
||||
expect(screen.getByTestId("current").textContent).toBe("1");
|
||||
});
|
||||
|
||||
it("should handle boundaries and disable buttons", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(PreviewControlsTest, { props: { current: 0, max: 2 } });
|
||||
|
||||
const prevBtn = screen.getByRole("button", { name: /previous slide/i });
|
||||
const nextBtn = screen.getByRole("button", { name: /next slide/i });
|
||||
|
||||
expect(prevBtn).toBeDisabled();
|
||||
|
||||
await user.click(nextBtn);
|
||||
|
||||
expect(screen.getByTestId("current").textContent).toBe("1");
|
||||
expect(nextBtn).toBeDisabled();
|
||||
expect(prevBtn).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
import { SlideDirection } from "$lib/constants";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PreviewControlsTest from "./PreviewControlsTest.svelte";
|
||||
|
||||
describe("PreviewControls logic", () => {
|
||||
it("should increment current and update direction on next click", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(PreviewControlsTest, { props: { current: 0, max: 3 } });
|
||||
|
||||
const nextBtn = screen.getByRole("button", { name: /next slide/i });
|
||||
|
||||
await user.click(nextBtn);
|
||||
|
||||
expect(screen.getByTestId("current").textContent).toBe("1");
|
||||
expect(screen.getByTestId("direction").textContent).toBe(SlideDirection.NEXT);
|
||||
});
|
||||
|
||||
it("should decrement current on prev click", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(PreviewControlsTest, { props: { current: 2, max: 3 } });
|
||||
|
||||
const prevBtn = screen.getByRole("button", { name: /previous slide/i });
|
||||
|
||||
await user.click(prevBtn);
|
||||
|
||||
expect(screen.getByTestId("current").textContent).toBe("1");
|
||||
});
|
||||
|
||||
it("should handle boundaries and disable buttons", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(PreviewControlsTest, { props: { current: 0, max: 2 } });
|
||||
|
||||
const prevBtn = screen.getByRole("button", { name: /previous slide/i });
|
||||
const nextBtn = screen.getByRole("button", { name: /next slide/i });
|
||||
|
||||
expect(prevBtn).toBeDisabled();
|
||||
|
||||
await user.click(nextBtn);
|
||||
|
||||
expect(screen.getByTestId("current").textContent).toBe("1");
|
||||
expect(nextBtn).toBeDisabled();
|
||||
expect(prevBtn).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import PreviewControls from "$components/panel/PreviewControls.svelte";
|
||||
|
||||
import { SlideDirection } from "$lib/constants";
|
||||
|
||||
let { current = 0, max = 5 } = $props();
|
||||
let direction = $state(SlideDirection.NEXT);
|
||||
</script>
|
||||
|
||||
<PreviewControls bind:current bind:direction {max} />
|
||||
|
||||
<div data-testid="current">{current}</div>
|
||||
<div data-testid="direction">{direction}</div>
|
||||
<script lang="ts">
|
||||
import PreviewControls from "$components/panel/PreviewControls.svelte";
|
||||
|
||||
import { SlideDirection } from "$lib/constants";
|
||||
|
||||
let { current = 0, max = 5 } = $props();
|
||||
let direction = $state(SlideDirection.NEXT);
|
||||
</script>
|
||||
|
||||
<PreviewControls bind:current bind:direction {max} />
|
||||
|
||||
<div data-testid="current">{current}</div>
|
||||
<div data-testid="direction">{direction}</div>
|
||||
|
||||
@@ -1,102 +1,103 @@
|
||||
import PreviewManager from "$components/panel/PreviewManager.svelte";
|
||||
import { downloadService } from "$services/downloadService";
|
||||
import { konvaStageState } from "$states/konvaStage.svelte";
|
||||
import { STATE_DATA } from "$states/persisted.svelte";
|
||||
import { textsState } from "$states/texts.svelte";
|
||||
import { render, screen, waitFor } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("$services/downloadService", () => ({
|
||||
downloadService: {
|
||||
downloadAll: vi.fn(),
|
||||
downloadPanel: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("./Preview.svelte", () => ({
|
||||
default: { render: () => ({}) },
|
||||
}));
|
||||
describe("PreviewManager Integration", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
textsState[STATE_DATA] = [];
|
||||
});
|
||||
|
||||
it("should show empty state by aria-label when no texts", () => {
|
||||
render(PreviewManager);
|
||||
expect(screen.getByLabelText(/Empty texts info/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should toggle navigation buttons availability based on texts length", async () => {
|
||||
textsState[STATE_DATA] = ["1", "2"];
|
||||
render(PreviewManager);
|
||||
|
||||
const nextBtn = screen.getByRole("button", { name: /next slide/i });
|
||||
const prevBtn = screen.getByRole("button", { name: /previous slide/i });
|
||||
|
||||
expect(nextBtn).not.toBeDisabled();
|
||||
expect(prevBtn).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should send correct data to downloadAll service", async () => {
|
||||
const user = userEvent.setup();
|
||||
textsState[STATE_DATA] = ["Apple", "Banana"];
|
||||
render(PreviewManager);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /download all/i }));
|
||||
|
||||
expect(downloadService.downloadAll).toHaveBeenCalledWith(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ filename: "Apple" }),
|
||||
expect.objectContaining({ filename: "Banana" }),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("should call downloadPanel with current active text", async () => {
|
||||
const user = userEvent.setup();
|
||||
textsState[STATE_DATA] = ["First", "Second"];
|
||||
konvaStageState.stage = { node: { id: "stage-ref" } } as any;
|
||||
|
||||
render(PreviewManager);
|
||||
|
||||
// Переходим на второй слайд
|
||||
await user.click(screen.getByRole("button", { name: /next slide/i }));
|
||||
await user.click(screen.getByRole("button", { name: /download current/i }));
|
||||
|
||||
expect(downloadService.downloadPanel).toHaveBeenCalledWith(expect.anything(), "Second");
|
||||
});
|
||||
|
||||
it("should return to empty state when texts are removed", async () => {
|
||||
textsState[STATE_DATA] = ["Temp"];
|
||||
render(PreviewManager);
|
||||
|
||||
expect(screen.queryByLabelText(/Empty texts info/i)).not.toBeInTheDocument();
|
||||
|
||||
textsState[STATE_DATA] = [];
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText(/Empty texts info/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should automatically correct current index on items deletion", async () => {
|
||||
const user = userEvent.setup();
|
||||
textsState[STATE_DATA] = ["1", "2", "3"];
|
||||
render(PreviewManager);
|
||||
|
||||
// Уходим на последний слайд
|
||||
await user.click(screen.getByRole("button", { name: /next slide/i }));
|
||||
await user.click(screen.getByRole("button", { name: /next slide/i }));
|
||||
|
||||
// Удаляем элементы. Индекс должен упасть с 2 до 0
|
||||
textsState[STATE_DATA] = ["Only one left"];
|
||||
|
||||
await waitFor(() => {
|
||||
// Проверяем, что кнопка "Next" заблокировалась (значит мы на последнем/единственном)
|
||||
expect(screen.getByRole("button", { name: /next slide/i })).toBeDisabled();
|
||||
expect(screen.getByRole("button", { name: /previous slide/i })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
import PreviewManager from "$components/panel/PreviewManager.svelte";
|
||||
import { downloadService } from "$services/downloadService";
|
||||
import { konvaStageState } from "$states/konvaStage.svelte";
|
||||
import { STATE_DATA } from "$states/persisted.svelte";
|
||||
import { textsState } from "$states/texts.svelte";
|
||||
import { render, screen, waitFor } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import type { Stage } from "svelte-konva";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("$services/downloadService", () => ({
|
||||
downloadService: {
|
||||
downloadAll: vi.fn(),
|
||||
downloadPanel: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("./Preview.svelte", () => ({
|
||||
default: { render: () => ({}) },
|
||||
}));
|
||||
describe("PreviewManager Integration", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
textsState[STATE_DATA] = [];
|
||||
});
|
||||
|
||||
it("should show empty state by aria-label when no texts", () => {
|
||||
render(PreviewManager);
|
||||
expect(screen.getByLabelText(/Empty texts info/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should toggle navigation buttons availability based on texts length", async () => {
|
||||
textsState[STATE_DATA] = ["1", "2"];
|
||||
render(PreviewManager);
|
||||
|
||||
const nextBtn = screen.getByRole("button", { name: /next slide/i });
|
||||
const prevBtn = screen.getByRole("button", { name: /previous slide/i });
|
||||
|
||||
expect(nextBtn).not.toBeDisabled();
|
||||
expect(prevBtn).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should send correct data to downloadAll service", async () => {
|
||||
const user = userEvent.setup();
|
||||
textsState[STATE_DATA] = ["Apple", "Banana"];
|
||||
render(PreviewManager);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /download all/i }));
|
||||
|
||||
expect(downloadService.downloadAll).toHaveBeenCalledWith(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ filename: "Apple" }),
|
||||
expect.objectContaining({ filename: "Banana" }),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("should call downloadPanel with current active text", async () => {
|
||||
const user = userEvent.setup();
|
||||
textsState[STATE_DATA] = ["First", "Second"];
|
||||
konvaStageState.stage = { node: { id: "stage-ref" } } as unknown as Stage;
|
||||
|
||||
render(PreviewManager);
|
||||
|
||||
// Переходим на второй слайд
|
||||
await user.click(screen.getByRole("button", { name: /next slide/i }));
|
||||
await user.click(screen.getByRole("button", { name: /download current/i }));
|
||||
|
||||
expect(downloadService.downloadPanel).toHaveBeenCalledWith(expect.anything(), "Second");
|
||||
});
|
||||
|
||||
it("should return to empty state when texts are removed", async () => {
|
||||
textsState[STATE_DATA] = ["Temp"];
|
||||
render(PreviewManager);
|
||||
|
||||
expect(screen.queryByLabelText(/Empty texts info/i)).not.toBeInTheDocument();
|
||||
|
||||
textsState[STATE_DATA] = [];
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText(/Empty texts info/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should automatically correct current index on items deletion", async () => {
|
||||
const user = userEvent.setup();
|
||||
textsState[STATE_DATA] = ["1", "2", "3"];
|
||||
render(PreviewManager);
|
||||
|
||||
// Уходим на последний слайд
|
||||
await user.click(screen.getByRole("button", { name: /next slide/i }));
|
||||
await user.click(screen.getByRole("button", { name: /next slide/i }));
|
||||
|
||||
// Удаляем элементы. Индекс должен упасть с 2 до 0
|
||||
textsState[STATE_DATA] = ["Only one left"];
|
||||
|
||||
await waitFor(() => {
|
||||
// Проверяем, что кнопка "Next" заблокировалась (значит мы на последнем/единственном)
|
||||
expect(screen.getByRole("button", { name: /next slide/i })).toBeDisabled();
|
||||
expect(screen.getByRole("button", { name: /previous slide/i })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import TextConfig from "$components/text/TextConfig.svelte";
|
||||
import { cleanup, render } from "@testing-library/svelte";
|
||||
import { afterEach, describe, it } from "vitest";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe("TextConfig.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(TextConfig);
|
||||
});
|
||||
});
|
||||
import TextConfig from "$components/text/TextConfig.svelte";
|
||||
import { cleanup, render } from "@testing-library/svelte";
|
||||
import { afterEach, describe, it } from "vitest";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe("TextConfig.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(TextConfig);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
import TextInlineEdit from "$components/text/TextInlineEdit.svelte";
|
||||
import { cleanup, render, screen } from "@testing-library/svelte";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe("TextInlineEdit.svelte", () => {
|
||||
it("should render with initial text", () => {
|
||||
render(TextInlineEdit, {
|
||||
props: {
|
||||
id: 1,
|
||||
text: "Test text",
|
||||
ondelete: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox");
|
||||
expect(input).toBeInTheDocument();
|
||||
expect(input).toHaveValue("Test text");
|
||||
});
|
||||
|
||||
it("should render with empty text", () => {
|
||||
render(TextInlineEdit, {
|
||||
props: {
|
||||
id: 1,
|
||||
text: "",
|
||||
ondelete: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox");
|
||||
expect(input).toHaveValue("");
|
||||
});
|
||||
});
|
||||
import TextInlineEdit from "$components/text/TextInlineEdit.svelte";
|
||||
import { cleanup, render, screen } from "@testing-library/svelte";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe("TextInlineEdit.svelte", () => {
|
||||
it("should render with initial text", () => {
|
||||
render(TextInlineEdit, {
|
||||
props: {
|
||||
id: 1,
|
||||
text: "Test text",
|
||||
ondelete: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox");
|
||||
expect(input).toBeInTheDocument();
|
||||
expect(input).toHaveValue("Test text");
|
||||
});
|
||||
|
||||
it("should render with empty text", () => {
|
||||
render(TextInlineEdit, {
|
||||
props: {
|
||||
id: 1,
|
||||
text: "",
|
||||
ondelete: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox");
|
||||
expect(input).toHaveValue("");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
import TextInput from "$components/text/TextInput.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("TextInput.svelte", () => {
|
||||
it("should render with initial value", () => {
|
||||
render(TextInput, {
|
||||
props: {
|
||||
text: "Test text",
|
||||
onenter: vi.fn(),
|
||||
ariaLabel: "Test input",
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /test input/i });
|
||||
expect(input).toBeInTheDocument();
|
||||
expect(input).toHaveValue("Test text");
|
||||
});
|
||||
|
||||
it("should update value on input", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(TextInput, {
|
||||
props: {
|
||||
text: "Initial",
|
||||
onenter: vi.fn(),
|
||||
ariaLabel: "Test input",
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /test input/i });
|
||||
await user.clear(input);
|
||||
await user.type(input, "Updated text");
|
||||
|
||||
expect(input).toHaveValue("Updated text");
|
||||
});
|
||||
|
||||
it("should call onenter only on Enter key and not on others", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onenterSpy = vi.fn();
|
||||
|
||||
render(TextInput, {
|
||||
props: {
|
||||
text: "test",
|
||||
onenter: onenterSpy,
|
||||
ariaLabel: "Test input",
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /test input/i });
|
||||
|
||||
await user.type(input, "abc");
|
||||
expect(onenterSpy).not.toHaveBeenCalled();
|
||||
|
||||
await user.keyboard("{Escape}");
|
||||
expect(onenterSpy).not.toHaveBeenCalled();
|
||||
|
||||
await user.type(input, "{Enter}");
|
||||
expect(onenterSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
import TextInput from "$components/text/TextInput.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("TextInput.svelte", () => {
|
||||
it("should render with initial value", () => {
|
||||
render(TextInput, {
|
||||
props: {
|
||||
text: "Test text",
|
||||
onenter: vi.fn(),
|
||||
ariaLabel: "Test input",
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /test input/i });
|
||||
expect(input).toBeInTheDocument();
|
||||
expect(input).toHaveValue("Test text");
|
||||
});
|
||||
|
||||
it("should update value on input", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(TextInput, {
|
||||
props: {
|
||||
text: "Initial",
|
||||
onenter: vi.fn(),
|
||||
ariaLabel: "Test input",
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /test input/i });
|
||||
await user.clear(input);
|
||||
await user.type(input, "Updated text");
|
||||
|
||||
expect(input).toHaveValue("Updated text");
|
||||
});
|
||||
|
||||
it("should call onenter only on Enter key and not on others", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onenterSpy = vi.fn();
|
||||
|
||||
render(TextInput, {
|
||||
props: {
|
||||
text: "test",
|
||||
onenter: onenterSpy,
|
||||
ariaLabel: "Test input",
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /test input/i });
|
||||
|
||||
await user.type(input, "abc");
|
||||
expect(onenterSpy).not.toHaveBeenCalled();
|
||||
|
||||
await user.keyboard("{Escape}");
|
||||
expect(onenterSpy).not.toHaveBeenCalled();
|
||||
|
||||
await user.type(input, "{Enter}");
|
||||
expect(onenterSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
import TextManager from "$components/text/TextManager.svelte";
|
||||
import { textsState } from "$states/texts.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("TextManager", () => {
|
||||
beforeEach(() => {
|
||||
textsState.clear();
|
||||
});
|
||||
|
||||
it("should add text to state and clear input on button click", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(TextManager);
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /input new text/i });
|
||||
const addButton = screen.getByRole("button", { name: /add text/i });
|
||||
|
||||
await user.type(input, "New Note");
|
||||
await user.click(addButton);
|
||||
|
||||
expect(textsState.texts).toHaveLength(1);
|
||||
expect(textsState.texts[0].text).toBe("New Note");
|
||||
expect(input).toHaveValue("");
|
||||
});
|
||||
|
||||
it("should add text on enter key", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(TextManager);
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /input new text/i });
|
||||
|
||||
await user.type(input, "Enter Note{Enter}");
|
||||
|
||||
expect(textsState.texts).toHaveLength(1);
|
||||
expect(textsState.texts[0].text).toBe("Enter Note");
|
||||
});
|
||||
|
||||
it("should display all items from state", async () => {
|
||||
textsState.addText("First");
|
||||
textsState.addText("Second");
|
||||
|
||||
render(TextManager);
|
||||
|
||||
const items = screen.getAllByRole("listitem");
|
||||
expect(items).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should remove text from state when delete button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
textsState.addText("To be deleted");
|
||||
|
||||
render(TextManager);
|
||||
|
||||
const deleteBtn = screen.getByRole("button", { name: /delete/i });
|
||||
await user.click(deleteBtn);
|
||||
|
||||
expect(textsState.texts).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
import TextManager from "$components/text/TextManager.svelte";
|
||||
import { textsState } from "$states/texts.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("TextManager", () => {
|
||||
beforeEach(() => {
|
||||
textsState.clear();
|
||||
});
|
||||
|
||||
it("should add text to state and clear input on button click", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(TextManager);
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /input new text/i });
|
||||
const addButton = screen.getByRole("button", { name: /add text/i });
|
||||
|
||||
await user.type(input, "New Note");
|
||||
await user.click(addButton);
|
||||
|
||||
expect(textsState.texts).toHaveLength(1);
|
||||
expect(textsState.texts[0].text).toBe("New Note");
|
||||
expect(input).toHaveValue("");
|
||||
});
|
||||
|
||||
it("should add text on enter key", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(TextManager);
|
||||
|
||||
const input = screen.getByRole("textbox", { name: /input new text/i });
|
||||
|
||||
await user.type(input, "Enter Note{Enter}");
|
||||
|
||||
expect(textsState.texts).toHaveLength(1);
|
||||
expect(textsState.texts[0].text).toBe("Enter Note");
|
||||
});
|
||||
|
||||
it("should display all items from state", async () => {
|
||||
textsState.addText("First");
|
||||
textsState.addText("Second");
|
||||
|
||||
render(TextManager);
|
||||
|
||||
const items = screen.getAllByRole("listitem");
|
||||
expect(items).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should remove text from state when delete button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
textsState.addText("To be deleted");
|
||||
|
||||
render(TextManager);
|
||||
|
||||
const deleteBtn = screen.getByRole("button", { name: /delete/i });
|
||||
await user.click(deleteBtn);
|
||||
|
||||
expect(textsState.texts).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
import { TextAlign } from "$lib/constants";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import AlignmentTest from "./AlignmentTest.svelte";
|
||||
|
||||
describe("Alignment.svelte", () => {
|
||||
it("should update state for every button by clicking in sequence", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(AlignmentTest);
|
||||
|
||||
const buttons = screen.getAllByRole("radio");
|
||||
const stateDisplay = screen.getByTestId("align-value");
|
||||
|
||||
const allButtonsToClick = [...buttons, buttons[0]];
|
||||
|
||||
for (const button of allButtonsToClick) {
|
||||
const label = button.getAttribute("aria-label")?.toLowerCase() || "";
|
||||
|
||||
await user.click(button);
|
||||
|
||||
const finalValue = stateDisplay.textContent?.toLowerCase() || "";
|
||||
expect(label).toContain(finalValue);
|
||||
}
|
||||
});
|
||||
|
||||
it("should have initial state from props", () => {
|
||||
render(AlignmentTest, { props: { align: TextAlign.RIGHT } });
|
||||
expect(screen.getByTestId("align-value").textContent).toBe(TextAlign.RIGHT);
|
||||
});
|
||||
|
||||
it("should sync with initial state and change state", async () => {
|
||||
const user = userEvent.setup();
|
||||
const { rerender } = render(AlignmentTest, { props: { align: TextAlign.RIGHT } });
|
||||
const stateDisplay = screen.getByTestId("align-value");
|
||||
|
||||
expect(stateDisplay.textContent).toBe(TextAlign.RIGHT);
|
||||
|
||||
await rerender({ align: TextAlign.CENTER });
|
||||
|
||||
const centerBtn = screen.getByRole("radio", { name: new RegExp(TextAlign.CENTER, "i") });
|
||||
expect(centerBtn).toBeChecked();
|
||||
});
|
||||
});
|
||||
import { TextAlign } from "$lib/constants";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import AlignmentTest from "./AlignmentTest.svelte";
|
||||
|
||||
describe("Alignment.svelte", () => {
|
||||
it("should update state for every button by clicking in sequence", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(AlignmentTest);
|
||||
|
||||
const buttons = screen.getAllByRole("radio");
|
||||
const stateDisplay = screen.getByTestId("align-value");
|
||||
|
||||
const allButtonsToClick = [...buttons, buttons[0]];
|
||||
|
||||
for (const button of allButtonsToClick) {
|
||||
const label = button.getAttribute("aria-label")?.toLowerCase() || "";
|
||||
|
||||
await user.click(button);
|
||||
|
||||
const finalValue = stateDisplay.textContent?.toLowerCase() || "";
|
||||
expect(label).toContain(finalValue);
|
||||
}
|
||||
});
|
||||
|
||||
it("should have initial state from props", () => {
|
||||
render(AlignmentTest, { props: { align: TextAlign.RIGHT } });
|
||||
expect(screen.getByTestId("align-value").textContent).toBe(TextAlign.RIGHT);
|
||||
});
|
||||
|
||||
it("should sync with initial state and change state", async () => {
|
||||
const { rerender } = render(AlignmentTest, { props: { align: TextAlign.RIGHT } });
|
||||
const stateDisplay = screen.getByTestId("align-value");
|
||||
|
||||
expect(stateDisplay.textContent).toBe(TextAlign.RIGHT);
|
||||
|
||||
await rerender({ align: TextAlign.CENTER });
|
||||
|
||||
const centerBtn = screen.getByRole("radio", { name: new RegExp(TextAlign.CENTER, "i") });
|
||||
expect(centerBtn).toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Alignment from "$components/ui/Alignment.svelte";
|
||||
|
||||
import { TextAlign } from "$lib/constants";
|
||||
|
||||
let { align = TextAlign.LEFT } = $props();
|
||||
</script>
|
||||
|
||||
<Alignment bind:align />
|
||||
|
||||
<div data-testid="align-value">{align}</div>
|
||||
<script lang="ts">
|
||||
import Alignment from "$components/ui/Alignment.svelte";
|
||||
|
||||
import { TextAlign } from "$lib/constants";
|
||||
|
||||
let { align = TextAlign.LEFT } = $props();
|
||||
</script>
|
||||
|
||||
<Alignment bind:align />
|
||||
|
||||
<div data-testid="align-value">{align}</div>
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
import Badge from "$components/ui/Badge.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("Badge.svelte", () => {
|
||||
it("should render with string text", () => {
|
||||
render(Badge, {
|
||||
props: {
|
||||
text: "Test Badge",
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("Test Badge")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render with number text", () => {
|
||||
render(Badge, {
|
||||
props: {
|
||||
text: 42,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("42")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render with empty string", () => {
|
||||
render(Badge, {
|
||||
props: {
|
||||
text: "",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should render with zero", () => {
|
||||
render(Badge, {
|
||||
props: {
|
||||
text: 0,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("0")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import Badge from "$components/ui/Badge.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("Badge.svelte", () => {
|
||||
it("should render with string text", () => {
|
||||
render(Badge, {
|
||||
props: {
|
||||
text: "Test Badge",
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("Test Badge")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render with number text", () => {
|
||||
render(Badge, {
|
||||
props: {
|
||||
text: 42,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("42")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render with empty string", () => {
|
||||
render(Badge, {
|
||||
props: {
|
||||
text: "",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should render with zero", () => {
|
||||
render(Badge, {
|
||||
props: {
|
||||
text: 0,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("0")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,95 +1,95 @@
|
||||
import Button from "$components/ui/Button.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import MockIcon from "./MockIcon.svelte";
|
||||
|
||||
describe("Button.svelte", () => {
|
||||
it("should render with icon and label", () => {
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Test Button",
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("Test Button")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: /test button/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render with icon only", () => {
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button.textContent.trim()).toBe("");
|
||||
});
|
||||
|
||||
it("should call onclick handler", async () => {
|
||||
const onclick = vi.fn();
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Click me",
|
||||
onclick,
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
await user.click(button);
|
||||
|
||||
expect(onclick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not throw error when clicked without onclick prop", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Click me",
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
|
||||
expect(() => user.click(button)).not.toThrow();
|
||||
});
|
||||
|
||||
it("should be disabled when disabled prop is true", () => {
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Disabled",
|
||||
disabled: true,
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should not be disabled by default", () => {
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Enabled",
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
expect(button).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
import Button from "$components/ui/Button.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import MockIcon from "./MockIcon.svelte";
|
||||
|
||||
describe("Button.svelte", () => {
|
||||
it("should render with icon and label", () => {
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Test Button",
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("Test Button")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: /test button/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render with icon only", () => {
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button.textContent.trim()).toBe("");
|
||||
});
|
||||
|
||||
it("should call onclick handler", async () => {
|
||||
const onclick = vi.fn();
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Click me",
|
||||
onclick,
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
await user.click(button);
|
||||
|
||||
expect(onclick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not throw error when clicked without onclick prop", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Click me",
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
|
||||
expect(() => user.click(button)).not.toThrow();
|
||||
});
|
||||
|
||||
it("should be disabled when disabled prop is true", () => {
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Disabled",
|
||||
disabled: true,
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should not be disabled by default", () => {
|
||||
render(Button, {
|
||||
props: {
|
||||
icon: MockIcon,
|
||||
label: "Enabled",
|
||||
ariaLabel: "Test button",
|
||||
},
|
||||
});
|
||||
|
||||
const button = screen.getByRole("button", { name: /test button/i });
|
||||
expect(button).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import ColorPicker from "$components/ui/ColorPicker.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
describe("ColorPicker.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(ColorPicker, {
|
||||
props: {
|
||||
value: "#ffffff",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
import ColorPicker from "$components/ui/ColorPicker.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
describe("ColorPicker.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(ColorPicker, {
|
||||
props: {
|
||||
value: "#ffffff",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg data-testid="mock-icon" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
</svg>
|
||||
<svg data-testid="mock-icon" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 96 B After Width: | Height: | Size: 93 B |
@@ -1,21 +1,21 @@
|
||||
import RangeSlider from "$components/ui/RangeSlider.svelte";
|
||||
import { fireEvent, render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("RangeSlider.svelte", () => {
|
||||
it("should call onchange handler", async () => {
|
||||
const onchange = vi.fn();
|
||||
|
||||
render(RangeSlider, {
|
||||
props: {
|
||||
value: 50,
|
||||
onchange,
|
||||
},
|
||||
});
|
||||
|
||||
const slider = screen.getByRole("slider");
|
||||
await fireEvent.change(slider, { target: { value: "75" } });
|
||||
|
||||
expect(onchange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
import RangeSlider from "$components/ui/RangeSlider.svelte";
|
||||
import { fireEvent, render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("RangeSlider.svelte", () => {
|
||||
it("should call onchange handler", async () => {
|
||||
const onchange = vi.fn();
|
||||
|
||||
render(RangeSlider, {
|
||||
props: {
|
||||
value: 50,
|
||||
onchange,
|
||||
},
|
||||
});
|
||||
|
||||
const slider = screen.getByRole("slider");
|
||||
await fireEvent.change(slider, { target: { value: "75" } });
|
||||
|
||||
expect(onchange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import SelectFont from "$components/ui/SelectFont.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
describe("SelectFont.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(SelectFont, {
|
||||
props: {
|
||||
value: "Arial",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
import SelectFont from "$components/ui/SelectFont.svelte";
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
describe("SelectFont.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(SelectFont, {
|
||||
props: {
|
||||
value: "Arial",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,68 +1,70 @@
|
||||
import * as Constants from "$lib/constants";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("Application Constants Logic", () => {
|
||||
describe("Typography & Panel Constraints", () => {
|
||||
it("should have default values within allowed boundaries", () => {
|
||||
const { TYPOGRAPHY, PANEL_SETTINGS } = Constants;
|
||||
|
||||
expect(TYPOGRAPHY.FONT_SIZE_DEFAULT).toBeGreaterThanOrEqual(TYPOGRAPHY.FONT_SIZE_MIN);
|
||||
expect(TYPOGRAPHY.FONT_SIZE_DEFAULT).toBeLessThanOrEqual(TYPOGRAPHY.FONT_SIZE_MAX);
|
||||
|
||||
expect(TYPOGRAPHY.FONT_FAMILIES).toContain(TYPOGRAPHY.FONT_FAMILY_DEFAULT);
|
||||
|
||||
expect(PANEL_SETTINGS.PANEL_HEIGHT_DEFAULT).toBeLessThanOrEqual(PANEL_SETTINGS.PANEL_HEIGHT_MAX);
|
||||
|
||||
expect(TYPOGRAPHY.VERTICAL_OFFSET_MAX).toBeGreaterThan(TYPOGRAPHY.VERTICAL_OFFSET_MIN);
|
||||
});
|
||||
|
||||
it("should not have duplicate values in lists", () => {
|
||||
const { TYPOGRAPHY, IMAGE_SETTINGS } = Constants;
|
||||
|
||||
const uniqueFonts = new Set(TYPOGRAPHY.FONT_FAMILIES);
|
||||
expect(uniqueFonts.size).toBe(TYPOGRAPHY.FONT_FAMILIES.length);
|
||||
|
||||
const uniqueFormats = new Set(IMAGE_SETTINGS.SUPPORTED_FORMATS);
|
||||
expect(uniqueFormats.size).toBe(IMAGE_SETTINGS.SUPPORTED_FORMATS.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Integrity & Formats", () => {
|
||||
it("should have valid format patterns for colors and mime-types", () => {
|
||||
const { TYPOGRAPHY, IMAGE_SETTINGS } = Constants;
|
||||
|
||||
expect(TYPOGRAPHY.TEXT_COLOR_DEFAULT).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
|
||||
IMAGE_SETTINGS.SUPPORTED_FORMATS.forEach((format) => {
|
||||
expect(format).toMatch(/^image\/(jpeg|jpg|png|webp|gif)$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Environment Specifics", () => {
|
||||
it("should set transition duration to 0 in test mode", () => {
|
||||
expect(Constants.TRANSITION_DURATION).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Global Contract (Snapshot)", () => {
|
||||
it("should match the previous configuration snapshot", () => {
|
||||
expect(Constants).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Assets Existence", () => {
|
||||
it("should verify that the default background image exists", () => {
|
||||
const { DEFAULT_BACKGROUND_IMAGE } = Constants.PANEL_SETTINGS;
|
||||
|
||||
const relativePath = DEFAULT_BACKGROUND_IMAGE.replace(/^\.\//, "");
|
||||
const fullPath = path.resolve(process.cwd(), "static", relativePath);
|
||||
|
||||
const exists = fs.existsSync(fullPath);
|
||||
|
||||
expect(exists, `Image not found at: ${fullPath}`).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
import * as Constants from "$lib/constants";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("Application Constants Logic", () => {
|
||||
describe("Typography & Panel Constraints", () => {
|
||||
it("should have default values within allowed boundaries", () => {
|
||||
const { TYPOGRAPHY, PANEL_SETTINGS } = Constants;
|
||||
|
||||
expect(TYPOGRAPHY.FONT_SIZE_DEFAULT).toBeGreaterThanOrEqual(TYPOGRAPHY.FONT_SIZE_MIN);
|
||||
expect(TYPOGRAPHY.FONT_SIZE_DEFAULT).toBeLessThanOrEqual(TYPOGRAPHY.FONT_SIZE_MAX);
|
||||
|
||||
expect(TYPOGRAPHY.FONT_FAMILIES).toContain(TYPOGRAPHY.FONT_FAMILY_DEFAULT);
|
||||
|
||||
expect(PANEL_SETTINGS.PANEL_HEIGHT_DEFAULT).toBeLessThanOrEqual(
|
||||
PANEL_SETTINGS.PANEL_HEIGHT_MAX,
|
||||
);
|
||||
|
||||
expect(TYPOGRAPHY.VERTICAL_OFFSET_MAX).toBeGreaterThan(TYPOGRAPHY.VERTICAL_OFFSET_MIN);
|
||||
});
|
||||
|
||||
it("should not have duplicate values in lists", () => {
|
||||
const { TYPOGRAPHY, IMAGE_SETTINGS } = Constants;
|
||||
|
||||
const uniqueFonts = new Set(TYPOGRAPHY.FONT_FAMILIES);
|
||||
expect(uniqueFonts.size).toBe(TYPOGRAPHY.FONT_FAMILIES.length);
|
||||
|
||||
const uniqueFormats = new Set(IMAGE_SETTINGS.SUPPORTED_FORMATS);
|
||||
expect(uniqueFormats.size).toBe(IMAGE_SETTINGS.SUPPORTED_FORMATS.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Integrity & Formats", () => {
|
||||
it("should have valid format patterns for colors and mime-types", () => {
|
||||
const { TYPOGRAPHY, IMAGE_SETTINGS } = Constants;
|
||||
|
||||
expect(TYPOGRAPHY.TEXT_COLOR_DEFAULT).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
|
||||
IMAGE_SETTINGS.SUPPORTED_FORMATS.forEach((format) => {
|
||||
expect(format).toMatch(/^image\/(jpeg|jpg|png|webp|gif)$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Environment Specifics", () => {
|
||||
it("should set transition duration to 0 in test mode", () => {
|
||||
expect(Constants.TRANSITION_DURATION).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Global Contract (Snapshot)", () => {
|
||||
it("should match the previous configuration snapshot", () => {
|
||||
expect(Constants).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Assets Existence", () => {
|
||||
it("should verify that the default background image exists", () => {
|
||||
const { DEFAULT_BACKGROUND_IMAGE } = Constants.PANEL_SETTINGS;
|
||||
|
||||
const relativePath = DEFAULT_BACKGROUND_IMAGE.replace(/^\.\//, "");
|
||||
const fullPath = path.resolve(process.cwd(), "static", relativePath);
|
||||
|
||||
const exists = fs.existsSync(fullPath);
|
||||
|
||||
expect(exists, `Image not found at: ${fullPath}`).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+207
-207
@@ -1,207 +1,207 @@
|
||||
import { AppError, CanvasError, ImageError, StorageError, TextError } from "$lib/error.types";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("error.types", () => {
|
||||
describe("AppError", () => {
|
||||
it("should create AppError with message and code", () => {
|
||||
const error = new AppError("Test error message", "TEST_CODE");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Test error message");
|
||||
expect(error.code).toBe("TEST_CODE");
|
||||
expect(error.details).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should create AppError with message, code and details", () => {
|
||||
const details = { userId: 123, action: "test" };
|
||||
const error = new AppError("Test error message", "TEST_CODE", details);
|
||||
|
||||
expect(error.details).toEqual(details);
|
||||
expect(error.message).toBe("Test error message");
|
||||
expect(error.code).toBe("TEST_CODE");
|
||||
});
|
||||
|
||||
it("should have stack trace", () => {
|
||||
const error = new AppError("Test error", "TEST_CODE");
|
||||
|
||||
expect(error.stack).toBeDefined();
|
||||
expect(typeof error.stack).toBe("string");
|
||||
});
|
||||
|
||||
it("should be throwable and catchable", () => {
|
||||
expect(() => {
|
||||
throw new AppError("Test error", "TEST_CODE");
|
||||
}).toThrow(AppError);
|
||||
});
|
||||
|
||||
it("should be catchable as Error", () => {
|
||||
expect(() => {
|
||||
throw new AppError("Test error", "TEST_CODE");
|
||||
}).toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ImageError", () => {
|
||||
it("should create ImageError with message", () => {
|
||||
const error = new ImageError("Image loading failed");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(ImageError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Image loading failed");
|
||||
expect(error.code).toBe("IMAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should have correct error code", () => {
|
||||
const error = new ImageError("Test");
|
||||
|
||||
expect(error.code).toBe("IMAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should be identifiable as ImageError", () => {
|
||||
const error = new ImageError("Test");
|
||||
|
||||
expect(error instanceof ImageError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("TextError", () => {
|
||||
it("should create TextError with message", () => {
|
||||
const error = new TextError("Text validation failed");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(TextError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Text validation failed");
|
||||
expect(error.code).toBe("TEXT_ERROR");
|
||||
});
|
||||
|
||||
it("should have correct error code", () => {
|
||||
const error = new TextError("Test");
|
||||
|
||||
expect(error.code).toBe("TEXT_ERROR");
|
||||
});
|
||||
|
||||
it("should be identifiable as TextError", () => {
|
||||
const error = new TextError("Test");
|
||||
|
||||
expect(error instanceof TextError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("CanvasError", () => {
|
||||
it("should create CanvasError with message", () => {
|
||||
const error = new CanvasError("Canvas rendering failed");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(CanvasError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Canvas rendering failed");
|
||||
expect(error.code).toBe("CANVAS_ERROR");
|
||||
});
|
||||
|
||||
it("should have correct error code", () => {
|
||||
const error = new CanvasError("Test");
|
||||
|
||||
expect(error.code).toBe("CANVAS_ERROR");
|
||||
});
|
||||
|
||||
it("should be identifiable as CanvasError", () => {
|
||||
const error = new CanvasError("Test");
|
||||
|
||||
expect(error instanceof CanvasError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("StorageError", () => {
|
||||
it("should create StorageError with message", () => {
|
||||
const error = new StorageError("Storage operation failed");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(StorageError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Storage operation failed");
|
||||
expect(error.code).toBe("STORAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should have correct error code", () => {
|
||||
const error = new StorageError("Test");
|
||||
|
||||
expect(error.code).toBe("STORAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should be identifiable as StorageError", () => {
|
||||
const error = new StorageError("Test");
|
||||
|
||||
expect(error instanceof StorageError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ErrorType union", () => {
|
||||
it("should accept all error types", () => {
|
||||
const errors: Array<AppError | ImageError | TextError | CanvasError | StorageError> = [
|
||||
new AppError("Test", "TEST"),
|
||||
new ImageError("Test"),
|
||||
new TextError("Test"),
|
||||
new CanvasError("Test"),
|
||||
new StorageError("Test"),
|
||||
];
|
||||
|
||||
errors.forEach((error) => {
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
});
|
||||
});
|
||||
|
||||
it("should allow type narrowing with instanceof", () => {
|
||||
const errors: Array<AppError | ImageError | TextError | CanvasError | StorageError> = [
|
||||
new ImageError("Test"),
|
||||
new TextError("Test"),
|
||||
];
|
||||
|
||||
const imageErrors = errors.filter((e) => e instanceof ImageError);
|
||||
const textErrors = errors.filter((e) => e instanceof TextError);
|
||||
|
||||
expect(imageErrors).toHaveLength(1);
|
||||
expect(textErrors).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Error handling patterns", () => {
|
||||
it("should handle errors in try-catch blocks", () => {
|
||||
let caughtError: AppError | null = null;
|
||||
|
||||
try {
|
||||
throw new ImageError("Image failed to load");
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
caughtError = error;
|
||||
}
|
||||
}
|
||||
|
||||
expect(caughtError).not.toBeNull();
|
||||
expect(caughtError?.code).toBe("IMAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should preserve error details through error handling", () => {
|
||||
const originalError = new AppError("Test", "TEST", { id: 123 });
|
||||
let caughtError: AppError | null = null;
|
||||
|
||||
try {
|
||||
throw originalError;
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
caughtError = error;
|
||||
}
|
||||
}
|
||||
|
||||
expect(caughtError?.details).toEqual({ id: 123 });
|
||||
});
|
||||
});
|
||||
});
|
||||
import { AppError, CanvasError, ImageError, StorageError, TextError } from "$lib/error.types";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("error.types", () => {
|
||||
describe("AppError", () => {
|
||||
it("should create AppError with message and code", () => {
|
||||
const error = new AppError("Test error message", "TEST_CODE");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Test error message");
|
||||
expect(error.code).toBe("TEST_CODE");
|
||||
expect(error.details).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should create AppError with message, code and details", () => {
|
||||
const details = { userId: 123, action: "test" };
|
||||
const error = new AppError("Test error message", "TEST_CODE", details);
|
||||
|
||||
expect(error.details).toEqual(details);
|
||||
expect(error.message).toBe("Test error message");
|
||||
expect(error.code).toBe("TEST_CODE");
|
||||
});
|
||||
|
||||
it("should have stack trace", () => {
|
||||
const error = new AppError("Test error", "TEST_CODE");
|
||||
|
||||
expect(error.stack).toBeDefined();
|
||||
expect(typeof error.stack).toBe("string");
|
||||
});
|
||||
|
||||
it("should be throwable and catchable", () => {
|
||||
expect(() => {
|
||||
throw new AppError("Test error", "TEST_CODE");
|
||||
}).toThrow(AppError);
|
||||
});
|
||||
|
||||
it("should be catchable as Error", () => {
|
||||
expect(() => {
|
||||
throw new AppError("Test error", "TEST_CODE");
|
||||
}).toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ImageError", () => {
|
||||
it("should create ImageError with message", () => {
|
||||
const error = new ImageError("Image loading failed");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(ImageError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Image loading failed");
|
||||
expect(error.code).toBe("IMAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should have correct error code", () => {
|
||||
const error = new ImageError("Test");
|
||||
|
||||
expect(error.code).toBe("IMAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should be identifiable as ImageError", () => {
|
||||
const error = new ImageError("Test");
|
||||
|
||||
expect(error instanceof ImageError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("TextError", () => {
|
||||
it("should create TextError with message", () => {
|
||||
const error = new TextError("Text validation failed");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(TextError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Text validation failed");
|
||||
expect(error.code).toBe("TEXT_ERROR");
|
||||
});
|
||||
|
||||
it("should have correct error code", () => {
|
||||
const error = new TextError("Test");
|
||||
|
||||
expect(error.code).toBe("TEXT_ERROR");
|
||||
});
|
||||
|
||||
it("should be identifiable as TextError", () => {
|
||||
const error = new TextError("Test");
|
||||
|
||||
expect(error instanceof TextError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("CanvasError", () => {
|
||||
it("should create CanvasError with message", () => {
|
||||
const error = new CanvasError("Canvas rendering failed");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(CanvasError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Canvas rendering failed");
|
||||
expect(error.code).toBe("CANVAS_ERROR");
|
||||
});
|
||||
|
||||
it("should have correct error code", () => {
|
||||
const error = new CanvasError("Test");
|
||||
|
||||
expect(error.code).toBe("CANVAS_ERROR");
|
||||
});
|
||||
|
||||
it("should be identifiable as CanvasError", () => {
|
||||
const error = new CanvasError("Test");
|
||||
|
||||
expect(error instanceof CanvasError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("StorageError", () => {
|
||||
it("should create StorageError with message", () => {
|
||||
const error = new StorageError("Storage operation failed");
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(StorageError);
|
||||
expect(error.name).toBe("AppError");
|
||||
expect(error.message).toBe("Storage operation failed");
|
||||
expect(error.code).toBe("STORAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should have correct error code", () => {
|
||||
const error = new StorageError("Test");
|
||||
|
||||
expect(error.code).toBe("STORAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should be identifiable as StorageError", () => {
|
||||
const error = new StorageError("Test");
|
||||
|
||||
expect(error instanceof StorageError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ErrorType union", () => {
|
||||
it("should accept all error types", () => {
|
||||
const errors: Array<AppError | ImageError | TextError | CanvasError | StorageError> = [
|
||||
new AppError("Test", "TEST"),
|
||||
new ImageError("Test"),
|
||||
new TextError("Test"),
|
||||
new CanvasError("Test"),
|
||||
new StorageError("Test"),
|
||||
];
|
||||
|
||||
errors.forEach((error) => {
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
});
|
||||
});
|
||||
|
||||
it("should allow type narrowing with instanceof", () => {
|
||||
const errors: Array<AppError | ImageError | TextError | CanvasError | StorageError> = [
|
||||
new ImageError("Test"),
|
||||
new TextError("Test"),
|
||||
];
|
||||
|
||||
const imageErrors = errors.filter((e) => e instanceof ImageError);
|
||||
const textErrors = errors.filter((e) => e instanceof TextError);
|
||||
|
||||
expect(imageErrors).toHaveLength(1);
|
||||
expect(textErrors).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Error handling patterns", () => {
|
||||
it("should handle errors in try-catch blocks", () => {
|
||||
let caughtError: AppError | null = null;
|
||||
|
||||
try {
|
||||
throw new ImageError("Image failed to load");
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
caughtError = error;
|
||||
}
|
||||
}
|
||||
|
||||
expect(caughtError).not.toBeNull();
|
||||
expect(caughtError?.code).toBe("IMAGE_ERROR");
|
||||
});
|
||||
|
||||
it("should preserve error details through error handling", () => {
|
||||
const originalError = new AppError("Test", "TEST", { id: 123 });
|
||||
let caughtError: AppError | null = null;
|
||||
|
||||
try {
|
||||
throw originalError;
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
caughtError = error;
|
||||
}
|
||||
}
|
||||
|
||||
expect(caughtError?.details).toEqual({ id: 123 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+155
-155
@@ -1,155 +1,155 @@
|
||||
import { AppError, CanvasError, ImageError, StorageError, TextError } from "$lib/error.types";
|
||||
import { createError, formatError, logError } from "$lib/utils/errorUtils";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("errorUtils", () => {
|
||||
describe("formatError", () => {
|
||||
it("should format AppError", () => {
|
||||
const error = new AppError("Test error", "TEST_CODE");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Test error");
|
||||
});
|
||||
|
||||
it("should format Error", () => {
|
||||
const error = new Error("Test error");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Test error");
|
||||
});
|
||||
|
||||
it("should format string error", () => {
|
||||
const error = "String error message";
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: String error message");
|
||||
});
|
||||
|
||||
it("should format unknown error", () => {
|
||||
const error = { custom: "object" };
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Произошла неизвестная ошибка");
|
||||
});
|
||||
|
||||
it("should use default message when not provided", () => {
|
||||
const error = new Error("Test error");
|
||||
const result = formatError(error);
|
||||
|
||||
expect(result).toBe("Произошла ошибка: Test error");
|
||||
});
|
||||
|
||||
it("should format ImageError", () => {
|
||||
const error = new ImageError("Image failed");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Image failed");
|
||||
});
|
||||
|
||||
it("should format TextError", () => {
|
||||
const error = new TextError("Text failed");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Text failed");
|
||||
});
|
||||
|
||||
it("should format CanvasError", () => {
|
||||
const error = new CanvasError("Canvas failed");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Canvas failed");
|
||||
});
|
||||
|
||||
it("should format StorageError", () => {
|
||||
const error = new StorageError("Storage failed");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Storage failed");
|
||||
});
|
||||
});
|
||||
|
||||
describe("createError", () => {
|
||||
it("should create AppError with message and code", () => {
|
||||
const error = createError("Test error", "TEST_CODE");
|
||||
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error.message).toBe("Test error");
|
||||
expect(error.code).toBe("TEST_CODE");
|
||||
expect(error.details).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should create AppError with message, code and details", () => {
|
||||
const details = { userId: 123, action: "test" };
|
||||
const error = createError("Test error", "TEST_CODE", details);
|
||||
|
||||
expect(error.details).toEqual(details);
|
||||
expect(error.message).toBe("Test error");
|
||||
expect(error.code).toBe("TEST_CODE");
|
||||
});
|
||||
});
|
||||
|
||||
describe("logError", () => {
|
||||
it("should log Error with stack", () => {
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
const error = new Error("Test error");
|
||||
|
||||
logError(error, "Test context");
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Error occurred:", {
|
||||
error,
|
||||
context: "Test context",
|
||||
timestamp: expect.any(String),
|
||||
stack: expect.any(String),
|
||||
});
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should log AppError with stack", () => {
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
const error = new AppError("Test error", "TEST_CODE");
|
||||
|
||||
logError(error, "Test context");
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Error occurred:", {
|
||||
error,
|
||||
context: "Test context",
|
||||
timestamp: expect.any(String),
|
||||
stack: expect.any(String),
|
||||
});
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should log string error without stack", () => {
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
logError("String error", "Test context");
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Error occurred:", {
|
||||
error: "String error",
|
||||
context: "Test context",
|
||||
timestamp: expect.any(String),
|
||||
stack: undefined,
|
||||
});
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should log error without context", () => {
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
const error = new Error("Test error");
|
||||
|
||||
logError(error);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Error occurred:", {
|
||||
error,
|
||||
context: undefined,
|
||||
timestamp: expect.any(String),
|
||||
stack: expect.any(String),
|
||||
});
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
import { AppError, CanvasError, ImageError, StorageError, TextError } from "$lib/error.types";
|
||||
import { createError, formatError, logError } from "$lib/utils/errorUtils";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("errorUtils", () => {
|
||||
describe("formatError", () => {
|
||||
it("should format AppError", () => {
|
||||
const error = new AppError("Test error", "TEST_CODE");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Test error");
|
||||
});
|
||||
|
||||
it("should format Error", () => {
|
||||
const error = new Error("Test error");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Test error");
|
||||
});
|
||||
|
||||
it("should format string error", () => {
|
||||
const error = "String error message";
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: String error message");
|
||||
});
|
||||
|
||||
it("should format unknown error", () => {
|
||||
const error = { custom: "object" };
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Произошла неизвестная ошибка");
|
||||
});
|
||||
|
||||
it("should use default message when not provided", () => {
|
||||
const error = new Error("Test error");
|
||||
const result = formatError(error);
|
||||
|
||||
expect(result).toBe("Произошла ошибка: Test error");
|
||||
});
|
||||
|
||||
it("should format ImageError", () => {
|
||||
const error = new ImageError("Image failed");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Image failed");
|
||||
});
|
||||
|
||||
it("should format TextError", () => {
|
||||
const error = new TextError("Text failed");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Text failed");
|
||||
});
|
||||
|
||||
it("should format CanvasError", () => {
|
||||
const error = new CanvasError("Canvas failed");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Canvas failed");
|
||||
});
|
||||
|
||||
it("should format StorageError", () => {
|
||||
const error = new StorageError("Storage failed");
|
||||
const result = formatError(error, "Default message");
|
||||
|
||||
expect(result).toBe("Default message: Storage failed");
|
||||
});
|
||||
});
|
||||
|
||||
describe("createError", () => {
|
||||
it("should create AppError with message and code", () => {
|
||||
const error = createError("Test error", "TEST_CODE");
|
||||
|
||||
expect(error).toBeInstanceOf(AppError);
|
||||
expect(error.message).toBe("Test error");
|
||||
expect(error.code).toBe("TEST_CODE");
|
||||
expect(error.details).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should create AppError with message, code and details", () => {
|
||||
const details = { userId: 123, action: "test" };
|
||||
const error = createError("Test error", "TEST_CODE", details);
|
||||
|
||||
expect(error.details).toEqual(details);
|
||||
expect(error.message).toBe("Test error");
|
||||
expect(error.code).toBe("TEST_CODE");
|
||||
});
|
||||
});
|
||||
|
||||
describe("logError", () => {
|
||||
it("should log Error with stack", () => {
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
const error = new Error("Test error");
|
||||
|
||||
logError(error, "Test context");
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Error occurred:", {
|
||||
error,
|
||||
context: "Test context",
|
||||
timestamp: expect.any(String),
|
||||
stack: expect.any(String),
|
||||
});
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should log AppError with stack", () => {
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
const error = new AppError("Test error", "TEST_CODE");
|
||||
|
||||
logError(error, "Test context");
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Error occurred:", {
|
||||
error,
|
||||
context: "Test context",
|
||||
timestamp: expect.any(String),
|
||||
stack: expect.any(String),
|
||||
});
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should log string error without stack", () => {
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
logError("String error", "Test context");
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Error occurred:", {
|
||||
error: "String error",
|
||||
context: "Test context",
|
||||
timestamp: expect.any(String),
|
||||
stack: undefined,
|
||||
});
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should log error without context", () => {
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
const error = new Error("Test error");
|
||||
|
||||
logError(error);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Error occurred:", {
|
||||
error,
|
||||
context: undefined,
|
||||
timestamp: expect.any(String),
|
||||
stack: expect.any(String),
|
||||
});
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import Layout from "$routes/+layout.svelte";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
<span data-testid="test-child">Hello</span>
|
||||
</Layout>
|
||||
<script>
|
||||
import Layout from "$routes/+layout.svelte";
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
<span data-testid="test-child">Hello</span>
|
||||
</Layout>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import Page from "$routes/+page.svelte";
|
||||
</script>
|
||||
|
||||
<Page />
|
||||
<script>
|
||||
import Page from "$routes/+page.svelte";
|
||||
</script>
|
||||
|
||||
<Page />
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { prerender, ssr } from "$routes/+layout";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("+layout.ts", () => {
|
||||
it("should export ssr as false", () => {
|
||||
expect(ssr).toBe(false);
|
||||
});
|
||||
|
||||
it("should export prerender as true", () => {
|
||||
expect(prerender).toBe(true);
|
||||
});
|
||||
});
|
||||
import { prerender, ssr } from "$routes/+layout";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("+layout.ts", () => {
|
||||
it("should export ssr as false", () => {
|
||||
expect(ssr).toBe(false);
|
||||
});
|
||||
|
||||
it("should export prerender as true", () => {
|
||||
expect(prerender).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import { themeState } from "$states/theme.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import LayoutTest from "./LayoutTest.svelte";
|
||||
|
||||
describe("+layout.svelte", () => {
|
||||
beforeEach(() => {
|
||||
themeState.current = "dark";
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should apply dark theme", () => {
|
||||
themeState.current = "dark";
|
||||
|
||||
expect(themeState.current).toBe("dark");
|
||||
});
|
||||
|
||||
it("should apply light theme", () => {
|
||||
themeState.current = "light";
|
||||
|
||||
expect(themeState.current).toBe("light");
|
||||
});
|
||||
|
||||
it("should toggle theme", () => {
|
||||
themeState.current = "dark";
|
||||
themeState.toggle();
|
||||
|
||||
expect(themeState.current).toBe("light");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Layout Component Coverage", () => {
|
||||
it("should render children snippet and initialize props", () => {
|
||||
render(LayoutTest);
|
||||
|
||||
expect(screen.getByTestId("test-child")).toBeInTheDocument();
|
||||
expect(screen.getByRole("banner")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import { themeState } from "$states/theme.svelte";
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import LayoutTest from "./LayoutTest.svelte";
|
||||
|
||||
describe("+layout.svelte", () => {
|
||||
beforeEach(() => {
|
||||
themeState.current = "dark";
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should apply dark theme", () => {
|
||||
themeState.current = "dark";
|
||||
|
||||
expect(themeState.current).toBe("dark");
|
||||
});
|
||||
|
||||
it("should apply light theme", () => {
|
||||
themeState.current = "light";
|
||||
|
||||
expect(themeState.current).toBe("light");
|
||||
});
|
||||
|
||||
it("should toggle theme", () => {
|
||||
themeState.current = "dark";
|
||||
themeState.toggle();
|
||||
|
||||
expect(themeState.current).toBe("light");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Layout Component Coverage", () => {
|
||||
it("should render children snippet and initialize props", () => {
|
||||
render(LayoutTest);
|
||||
|
||||
expect(screen.getByTestId("test-child")).toBeInTheDocument();
|
||||
expect(screen.getByRole("banner")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PageTest from "./PageTest.svelte";
|
||||
|
||||
describe("+page.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(PageTest);
|
||||
expect(screen.getByText("Тексты панелей")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import { render, screen } from "@testing-library/svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import PageTest from "./PageTest.svelte";
|
||||
|
||||
describe("+page.svelte", () => {
|
||||
it("should render without crashing", () => {
|
||||
render(PageTest);
|
||||
expect(screen.getByText("Тексты панелей")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,125 +1,131 @@
|
||||
import { DownloadService, type DownloadItem } from "$services/downloadService";
|
||||
import { saveAs } from "file-saver";
|
||||
import JSZip from "jszip";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("file-saver", () => {
|
||||
return {
|
||||
saveAs: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("jszip", () => {
|
||||
const mockZipInstance = {
|
||||
file: vi.fn().mockReturnThis(),
|
||||
generateAsync: vi.fn().mockResolvedValue(new Blob([])),
|
||||
};
|
||||
|
||||
return {
|
||||
default: vi.fn(function () {
|
||||
return mockZipInstance;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe("DownloadService", () => {
|
||||
let service: DownloadService;
|
||||
let mockKonvaStage: any;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new DownloadService();
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockKonvaStage = {
|
||||
toBlob: vi.fn(({ callback }) => {
|
||||
callback(new Blob(["test image data"], { type: "image/png" }));
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe("downloadPanel", () => {
|
||||
it("should export panel successfully", async () => {
|
||||
const result = await service.downloadPanel(mockKonvaStage, "test-panel-1");
|
||||
|
||||
const [blobArg, fileNameArg] = vi.mocked(saveAs).mock.calls[0];
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockKonvaStage.toBlob).toHaveBeenCalled();
|
||||
expect(blobArg instanceof Blob).toBe(true);
|
||||
expect(fileNameArg).toBe("test-panel-1.png");
|
||||
});
|
||||
|
||||
it("should handle Konva stage without toBlob method", async () => {
|
||||
const invalidStage = { toBlob: undefined };
|
||||
|
||||
const result = await service.downloadPanel(invalidStage as any, "test-panel-1.png");
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error).toContain("Konva Stage не найден или не поддерживает toBlob");
|
||||
}
|
||||
});
|
||||
|
||||
it("should handle blob creation failure", async () => {
|
||||
mockKonvaStage.toBlob = vi.fn(({ callback }) => {
|
||||
callback(null);
|
||||
});
|
||||
|
||||
const result = await service.downloadPanel(mockKonvaStage, "test-panel-1.png");
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error).toContain("Не удалось создать изображение");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("downloadAllPanels", () => {
|
||||
it("should handle successful download of multiple panels", async () => {
|
||||
const panels: Array<DownloadItem> = [{ filename: "test-panel-1", stage: mockKonvaStage }];
|
||||
const result = await service.downloadAll(panels);
|
||||
const [blobArg, fileNameArg] = vi.mocked(saveAs).mock.calls[0];
|
||||
const zipInstance = vi.mocked(JSZip).mock.instances[0];
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockKonvaStage.toBlob).toHaveBeenCalled();
|
||||
expect(blobArg instanceof Blob).toBe(true);
|
||||
expect(fileNameArg).toBe("panels.zip");
|
||||
expect(zipInstance.file).toHaveBeenCalledTimes(1);
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-1.png", expect.anything());
|
||||
});
|
||||
|
||||
it("should add to zip all files", async () => {
|
||||
const panels: Array<DownloadItem> = [
|
||||
{ filename: "test-panel-1", stage: mockKonvaStage },
|
||||
{ filename: "test-panel-2", stage: mockKonvaStage },
|
||||
{ filename: "test-panel-3", stage: mockKonvaStage },
|
||||
{ filename: "test-panel-4", stage: mockKonvaStage },
|
||||
{ filename: "test-panel-5", stage: mockKonvaStage },
|
||||
];
|
||||
const result = await service.downloadAll(panels);
|
||||
const [blobArg, fileNameArg] = vi.mocked(saveAs).mock.calls[0];
|
||||
const zipInstance = vi.mocked(JSZip).mock.instances[0];
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockKonvaStage.toBlob).toHaveBeenCalledTimes(5);
|
||||
expect(zipInstance.file).toHaveBeenCalledTimes(5);
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-1.png", expect.anything());
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-2.png", expect.anything());
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-3.png", expect.anything());
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-4.png", expect.anything());
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-5.png", expect.anything());
|
||||
});
|
||||
|
||||
it("should handle failure", async () => {
|
||||
const invalidStage = { toBlob: undefined };
|
||||
const panels: Array<DownloadItem> = [{ filename: "test-panel-1", stage: invalidStage as any }];
|
||||
const result = await service.downloadAll(panels);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error).toContain("Ошибка сохранения архива");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
import { DownloadService, type DownloadItem } from "$services/downloadService";
|
||||
import { saveAs } from "file-saver";
|
||||
import JSZip from "jszip";
|
||||
import type { Stage } from "konva/lib/Stage";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("file-saver", () => {
|
||||
return {
|
||||
saveAs: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("jszip", () => {
|
||||
const mockZipInstance = {
|
||||
file: vi.fn().mockReturnThis(),
|
||||
generateAsync: vi.fn().mockResolvedValue(new Blob([])),
|
||||
};
|
||||
|
||||
return {
|
||||
default: vi.fn(function () {
|
||||
return mockZipInstance;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe("DownloadService", () => {
|
||||
let service: DownloadService;
|
||||
let mockKonvaStage: Stage;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new DownloadService();
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockKonvaStage = {
|
||||
toBlob: vi.fn(async ({ callback }) => {
|
||||
callback(new Blob(["test image data"], { type: "image/png" }));
|
||||
}),
|
||||
} as unknown as Stage;
|
||||
});
|
||||
|
||||
describe("downloadPanel", () => {
|
||||
it("should export panel successfully", async () => {
|
||||
const result = await service.downloadPanel(mockKonvaStage, "test-panel-1");
|
||||
|
||||
const [blobArg, fileNameArg] = vi.mocked(saveAs).mock.calls[0];
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockKonvaStage.toBlob).toHaveBeenCalled();
|
||||
expect(blobArg instanceof Blob).toBe(true);
|
||||
expect(fileNameArg).toBe("test-panel-1.png");
|
||||
});
|
||||
|
||||
it("should handle Konva stage without toBlob method", async () => {
|
||||
const invalidStage = { toBlob: undefined };
|
||||
|
||||
const result = await service.downloadPanel(
|
||||
invalidStage as unknown as Stage,
|
||||
"test-panel-1.png",
|
||||
);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error).toContain("Konva Stage не найден или не поддерживает toBlob");
|
||||
}
|
||||
});
|
||||
|
||||
it("should handle blob creation failure", async () => {
|
||||
mockKonvaStage.toBlob = vi.fn(async ({ callback }) => {
|
||||
callback(null);
|
||||
});
|
||||
|
||||
const result = await service.downloadPanel(mockKonvaStage, "test-panel-1.png");
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error).toContain("Не удалось создать изображение");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("downloadAllPanels", () => {
|
||||
it("should handle successful download of multiple panels", async () => {
|
||||
const panels: Array<DownloadItem> = [{ filename: "test-panel-1", stage: mockKonvaStage }];
|
||||
const result = await service.downloadAll(panels);
|
||||
const [blobArg, fileNameArg] = vi.mocked(saveAs).mock.calls[0];
|
||||
const zipInstance = vi.mocked(JSZip).mock.instances[0];
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockKonvaStage.toBlob).toHaveBeenCalled();
|
||||
expect(blobArg instanceof Blob).toBe(true);
|
||||
expect(fileNameArg).toBe("panels.zip");
|
||||
expect(zipInstance.file).toHaveBeenCalledTimes(1);
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-1.png", expect.anything());
|
||||
});
|
||||
|
||||
it("should add to zip all files", async () => {
|
||||
const panels: Array<DownloadItem> = [
|
||||
{ filename: "test-panel-1", stage: mockKonvaStage },
|
||||
{ filename: "test-panel-2", stage: mockKonvaStage },
|
||||
{ filename: "test-panel-3", stage: mockKonvaStage },
|
||||
{ filename: "test-panel-4", stage: mockKonvaStage },
|
||||
{ filename: "test-panel-5", stage: mockKonvaStage },
|
||||
];
|
||||
const result = await service.downloadAll(panels);
|
||||
// const [blobArg, fileNameArg] = vi.mocked(saveAs).mock.calls[0];
|
||||
const zipInstance = vi.mocked(JSZip).mock.instances[0];
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockKonvaStage.toBlob).toHaveBeenCalledTimes(5);
|
||||
expect(zipInstance.file).toHaveBeenCalledTimes(5);
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-1.png", expect.anything());
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-2.png", expect.anything());
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-3.png", expect.anything());
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-4.png", expect.anything());
|
||||
expect(zipInstance.file).toHaveBeenCalledWith("test-panel-5.png", expect.anything());
|
||||
});
|
||||
|
||||
it("should handle failure", async () => {
|
||||
const invalidStage = { toBlob: undefined };
|
||||
const panels: Array<DownloadItem> = [
|
||||
{ filename: "test-panel-1", stage: invalidStage as unknown as Stage },
|
||||
];
|
||||
const result = await service.downloadAll(panels);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error).toContain("Ошибка сохранения архива");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,192 +1,216 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
import { PANEL_SETTINGS } from "$lib/constants";
|
||||
import { imageConfigState, ImageConfigState } from "$states/imageConfig.svelte";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
type ImageEventHandler = ((this: HTMLImageElement, ev?: Event) => void) | null;
|
||||
type ImageErrorEventHandler =
|
||||
| ((
|
||||
this: HTMLImageElement,
|
||||
ev?: string | Event,
|
||||
source?: string,
|
||||
lineno?: number,
|
||||
colno?: number,
|
||||
error?: Error,
|
||||
) => void)
|
||||
| null;
|
||||
|
||||
let lastOnload: ImageEventHandler = null;
|
||||
let lastOnerror: ImageErrorEventHandler = null;
|
||||
|
||||
vi.stubGlobal(
|
||||
"Image",
|
||||
class {
|
||||
_onload: ImageEventHandler = null;
|
||||
_onerror: ImageErrorEventHandler = null;
|
||||
_src: string = "";
|
||||
crossOrigin: string = "";
|
||||
|
||||
set src(val: string) {
|
||||
this._src = val;
|
||||
if (val.includes("error")) {
|
||||
setTimeout(
|
||||
() => this._onerror?.call(this as unknown as HTMLImageElement, new Event("error")),
|
||||
1,
|
||||
);
|
||||
} else {
|
||||
setTimeout(
|
||||
() => this._onload?.call(this as unknown as HTMLImageElement, new Event("load")),
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
get src() {
|
||||
return this._src;
|
||||
}
|
||||
|
||||
set onload(val: ImageEventHandler) {
|
||||
this._onload = val;
|
||||
if (val) lastOnload = val;
|
||||
}
|
||||
get onload() {
|
||||
return this._onload;
|
||||
}
|
||||
|
||||
set onerror(val: ImageErrorEventHandler) {
|
||||
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.call(new Image() as HTMLImageElement, new Event("load"));
|
||||
}
|
||||
|
||||
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.call(new Image() as HTMLImageElement, new Event("load"));
|
||||
}
|
||||
|
||||
await expect(promise).rejects.toThrow();
|
||||
expect(imageConfigState.imageReady).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { konvaAllStagesState } from "$states/konvaAllStages.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("konvaAllStages.svelte", () => {
|
||||
it("should import successfully", () => {
|
||||
expect(konvaAllStagesState).toBeDefined();
|
||||
});
|
||||
});
|
||||
import { konvaAllStagesState } from "$states/konvaAllStages.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("konvaAllStages.svelte", () => {
|
||||
it("should import successfully", () => {
|
||||
expect(konvaAllStagesState).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,54 +1,14 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
import { konvaStageState } from "$states/konvaStage.svelte";
|
||||
import type { Stage } from "svelte-konva";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("konvaStageState integration", () => {
|
||||
it("should work", () => {
|
||||
expect(konvaStageState.stage).toBeUndefined();
|
||||
|
||||
const mock = { name: "stage" } as unknown as Stage;
|
||||
konvaStageState.stage = mock;
|
||||
|
||||
expect(konvaStageState.stage).toStrictEqual(mock);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,158 +1,167 @@
|
||||
import { STATE_DATA, withPersistence } from "$states/persisted.svelte";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const localStorageMock = (() => {
|
||||
let store: Record<string, string> = {};
|
||||
return {
|
||||
getItem: vi.fn((key: string) => store[key] || null),
|
||||
setItem: vi.fn((key: string, value: string) => {
|
||||
store[key] = value;
|
||||
}),
|
||||
removeItem: vi.fn((key: string) => {
|
||||
delete store[key];
|
||||
}),
|
||||
clear: vi.fn(() => {
|
||||
store = {};
|
||||
}),
|
||||
};
|
||||
})();
|
||||
|
||||
Object.defineProperty(globalThis, "localStorage", {
|
||||
value: localStorageMock,
|
||||
});
|
||||
|
||||
describe("persisted.svelte", () => {
|
||||
beforeEach(() => {
|
||||
localStorageMock.clear();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("withPersistence", () => {
|
||||
it("should return state unchanged when not in browser", () => {
|
||||
const mockState = {
|
||||
[STATE_DATA]: { test: "value" },
|
||||
};
|
||||
const result = withPersistence("test-key", mockState);
|
||||
expect(result).toBe(mockState);
|
||||
});
|
||||
|
||||
it("should restore state from localStorage when valid JSON exists", () => {
|
||||
const savedData = { value: 42, name: "test" };
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(savedData));
|
||||
|
||||
const state = {
|
||||
[STATE_DATA]: { value: 0, name: "" },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state);
|
||||
|
||||
expect(state[STATE_DATA]).toEqual(savedData);
|
||||
expect(localStorageMock.getItem).toHaveBeenCalledWith("test-key");
|
||||
});
|
||||
|
||||
it("should handle corrupted JSON in localStorage gracefully", () => {
|
||||
localStorageMock.getItem.mockReturnValue("{ invalid json");
|
||||
|
||||
const state = {
|
||||
[STATE_DATA]: { value: 0 },
|
||||
};
|
||||
|
||||
expect(() => withPersistence("test-key", state)).not.toThrow();
|
||||
expect(state[STATE_DATA]).toEqual({ value: 0 });
|
||||
});
|
||||
|
||||
it("should handle empty localStorage (no saved data)", () => {
|
||||
localStorageMock.getItem.mockReturnValue(null);
|
||||
|
||||
const state = {
|
||||
[STATE_DATA]: { value: 0 },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state);
|
||||
|
||||
expect(state[STATE_DATA]).toEqual({ value: 0 });
|
||||
});
|
||||
|
||||
it("should save state to localStorage with debounce", async () => {
|
||||
vi.useFakeTimers();
|
||||
const state = {
|
||||
[STATE_DATA]: { count: 1 },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state, 500);
|
||||
|
||||
state[STATE_DATA] = { count: 2 };
|
||||
await Promise.resolve();
|
||||
|
||||
vi.advanceTimersByTime(500);
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith("test-key", JSON.stringify({ count: 2 }));
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should save state immediately when debounce is 0", async () => {
|
||||
const state = {
|
||||
[STATE_DATA]: { count: 1 },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state, 0);
|
||||
|
||||
state[STATE_DATA] = { count: 2 };
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith("test-key", JSON.stringify({ count: 2 }));
|
||||
});
|
||||
|
||||
it("should cleanup timeout on state change during debounce", async () => {
|
||||
vi.useFakeTimers();
|
||||
const data = $state({ count: 1 });
|
||||
|
||||
const state = {
|
||||
get [STATE_DATA]() {
|
||||
return data;
|
||||
},
|
||||
set [STATE_DATA](v) {
|
||||
data.count = v.count;
|
||||
},
|
||||
};
|
||||
|
||||
withPersistence("test-key", state, 500);
|
||||
|
||||
state[STATE_DATA] = { count: 2 };
|
||||
await Promise.resolve();
|
||||
|
||||
state[STATE_DATA] = { count: 3 };
|
||||
await Promise.resolve();
|
||||
|
||||
vi.runOnlyPendingTimers();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledTimes(1);
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith("test-key", JSON.stringify({ count: 3 }));
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should use DEBOUNCE_DURATION constant as default", async () => {
|
||||
const state = {
|
||||
[STATE_DATA]: { test: true },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state);
|
||||
|
||||
state[STATE_DATA] = { test: false };
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
import { STATE_DATA, withPersistence } from "$states/persisted.svelte";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const localStorageMock = (() => {
|
||||
let store: Record<string, string> = {};
|
||||
return {
|
||||
getItem: vi.fn((key: string) => store[key] || null),
|
||||
setItem: vi.fn((key: string, value: string) => {
|
||||
store[key] = value;
|
||||
}),
|
||||
removeItem: vi.fn((key: string) => {
|
||||
delete store[key];
|
||||
}),
|
||||
clear: vi.fn(() => {
|
||||
store = {};
|
||||
}),
|
||||
};
|
||||
})();
|
||||
|
||||
Object.defineProperty(globalThis, "localStorage", {
|
||||
value: localStorageMock,
|
||||
});
|
||||
|
||||
describe("persisted.svelte", () => {
|
||||
beforeEach(() => {
|
||||
localStorageMock.clear();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("withPersistence", () => {
|
||||
it("should return state unchanged when not in browser", () => {
|
||||
const mockState = {
|
||||
[STATE_DATA]: { test: "value" },
|
||||
};
|
||||
const result = withPersistence("test-key", mockState);
|
||||
expect(result).toBe(mockState);
|
||||
});
|
||||
|
||||
it("should restore state from localStorage when valid JSON exists", () => {
|
||||
const savedData = { value: 42, name: "test" };
|
||||
localStorageMock.getItem.mockReturnValue(JSON.stringify(savedData));
|
||||
|
||||
const state = {
|
||||
[STATE_DATA]: { value: 0, name: "" },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state);
|
||||
|
||||
expect(state[STATE_DATA]).toEqual(savedData);
|
||||
expect(localStorageMock.getItem).toHaveBeenCalledWith("test-key");
|
||||
});
|
||||
|
||||
it("should handle corrupted JSON in localStorage gracefully", () => {
|
||||
localStorageMock.getItem.mockReturnValue("{ invalid json");
|
||||
|
||||
const state = {
|
||||
[STATE_DATA]: { value: 0 },
|
||||
};
|
||||
|
||||
expect(() => withPersistence("test-key", state)).not.toThrow();
|
||||
expect(state[STATE_DATA]).toEqual({ value: 0 });
|
||||
});
|
||||
|
||||
it("should handle empty localStorage (no saved data)", () => {
|
||||
localStorageMock.getItem.mockReturnValue(null);
|
||||
|
||||
const state = {
|
||||
[STATE_DATA]: { value: 0 },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state);
|
||||
|
||||
expect(state[STATE_DATA]).toEqual({ value: 0 });
|
||||
});
|
||||
|
||||
it("should save state to localStorage with debounce", async () => {
|
||||
vi.useFakeTimers();
|
||||
const state = {
|
||||
[STATE_DATA]: { count: 1 },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state, 500);
|
||||
|
||||
state[STATE_DATA] = { count: 2 };
|
||||
await Promise.resolve();
|
||||
|
||||
vi.advanceTimersByTime(500);
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
||||
"test-key",
|
||||
JSON.stringify({ count: 2 }),
|
||||
);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should save state immediately when debounce is 0", async () => {
|
||||
const state = {
|
||||
[STATE_DATA]: { count: 1 },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state, 0);
|
||||
|
||||
state[STATE_DATA] = { count: 2 };
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
||||
"test-key",
|
||||
JSON.stringify({ count: 2 }),
|
||||
);
|
||||
});
|
||||
|
||||
it("should cleanup timeout on state change during debounce", async () => {
|
||||
vi.useFakeTimers();
|
||||
const data = $state({ count: 1 });
|
||||
|
||||
const state = {
|
||||
get [STATE_DATA]() {
|
||||
return data;
|
||||
},
|
||||
set [STATE_DATA](v) {
|
||||
data.count = v.count;
|
||||
},
|
||||
};
|
||||
|
||||
withPersistence("test-key", state, 500);
|
||||
|
||||
state[STATE_DATA] = { count: 2 };
|
||||
await Promise.resolve();
|
||||
|
||||
state[STATE_DATA] = { count: 3 };
|
||||
await Promise.resolve();
|
||||
|
||||
vi.runOnlyPendingTimers();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledTimes(1);
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
||||
"test-key",
|
||||
JSON.stringify({ count: 3 }),
|
||||
);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should use DEBOUNCE_DURATION constant as default", async () => {
|
||||
const state = {
|
||||
[STATE_DATA]: { test: true },
|
||||
};
|
||||
|
||||
withPersistence("test-key", state);
|
||||
|
||||
state[STATE_DATA] = { test: false };
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
expect(localStorageMock.setItem).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,79 +1,79 @@
|
||||
import { TextAlign } from "$lib/constants";
|
||||
import type { HexColor } from "$lib/types";
|
||||
import { STATE_DATA } from "$states/persisted.svelte";
|
||||
import { textConfigState } from "$states/textConfig.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const TEXT_ALIGN_VALUES = Object.values(TextAlign);
|
||||
|
||||
describe("textConfig.svelte", () => {
|
||||
describe("initial state", () => {
|
||||
it("should have valid initial state structure", () => {
|
||||
expect(typeof textConfigState.fontSize).toBe("number");
|
||||
expect(typeof textConfigState.fontFamily).toBe("string");
|
||||
expect(typeof textConfigState.color).toBe("string");
|
||||
expect(textConfigState.color).toMatch(/^#[0-9a-fA-F]{6}$/);
|
||||
expect(TEXT_ALIGN_VALUES).toContain(textConfigState.align);
|
||||
expect(typeof textConfigState.paddingX).toBe("number");
|
||||
expect(typeof textConfigState.offsetY).toBe("number");
|
||||
});
|
||||
});
|
||||
|
||||
describe("property setters", () => {
|
||||
it("should update all properties correctly", () => {
|
||||
textConfigState.fontSize = 32;
|
||||
textConfigState.fontFamily = "Roboto";
|
||||
textConfigState.color = "#ff0000" as HexColor;
|
||||
textConfigState.align = TextAlign.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(TextAlign.LEFT);
|
||||
expect(textConfigState.paddingX).toBe(20);
|
||||
expect(textConfigState.offsetY).toBe(-10);
|
||||
});
|
||||
});
|
||||
|
||||
describe("STATE_DATA", () => {
|
||||
it("should serialize and deserialize state", () => {
|
||||
// Set custom values
|
||||
textConfigState.fontSize = 18;
|
||||
textConfigState.fontFamily = "Georgia";
|
||||
textConfigState.color = "#00ff00" as HexColor;
|
||||
textConfigState.align = TextAlign.RIGHT;
|
||||
textConfigState.paddingX = 5;
|
||||
textConfigState.offsetY = 15;
|
||||
|
||||
// Get serialized data
|
||||
const data = textConfigState[STATE_DATA];
|
||||
expect(data).toEqual({
|
||||
fontSize: 18,
|
||||
fontFamily: "Georgia",
|
||||
color: "#00ff00",
|
||||
align: TextAlign.RIGHT,
|
||||
paddingX: 5,
|
||||
offsetY: 15,
|
||||
});
|
||||
|
||||
// Restore from serialized data
|
||||
textConfigState[STATE_DATA] = {
|
||||
fontSize: 24,
|
||||
fontFamily: "Arial",
|
||||
color: "#ffffff" as HexColor,
|
||||
align: TextAlign.CENTER,
|
||||
paddingX: 10,
|
||||
offsetY: 0,
|
||||
};
|
||||
|
||||
expect(textConfigState.fontSize).toBe(24);
|
||||
expect(textConfigState.fontFamily).toBe("Arial");
|
||||
expect(textConfigState.color).toBe("#ffffff");
|
||||
expect(textConfigState.align).toBe(TextAlign.CENTER);
|
||||
expect(textConfigState.paddingX).toBe(10);
|
||||
expect(textConfigState.offsetY).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
import { TextAlign } from "$lib/constants";
|
||||
import type { HexColor } from "$lib/types";
|
||||
import { STATE_DATA } from "$states/persisted.svelte";
|
||||
import { textConfigState } from "$states/textConfig.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const TEXT_ALIGN_VALUES = Object.values(TextAlign);
|
||||
|
||||
describe("textConfig.svelte", () => {
|
||||
describe("initial state", () => {
|
||||
it("should have valid initial state structure", () => {
|
||||
expect(typeof textConfigState.fontSize).toBe("number");
|
||||
expect(typeof textConfigState.fontFamily).toBe("string");
|
||||
expect(typeof textConfigState.color).toBe("string");
|
||||
expect(textConfigState.color).toMatch(/^#[0-9a-fA-F]{6}$/);
|
||||
expect(TEXT_ALIGN_VALUES).toContain(textConfigState.align);
|
||||
expect(typeof textConfigState.paddingX).toBe("number");
|
||||
expect(typeof textConfigState.offsetY).toBe("number");
|
||||
});
|
||||
});
|
||||
|
||||
describe("property setters", () => {
|
||||
it("should update all properties correctly", () => {
|
||||
textConfigState.fontSize = 32;
|
||||
textConfigState.fontFamily = "Roboto";
|
||||
textConfigState.color = "#ff0000" as HexColor;
|
||||
textConfigState.align = TextAlign.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(TextAlign.LEFT);
|
||||
expect(textConfigState.paddingX).toBe(20);
|
||||
expect(textConfigState.offsetY).toBe(-10);
|
||||
});
|
||||
});
|
||||
|
||||
describe("STATE_DATA", () => {
|
||||
it("should serialize and deserialize state", () => {
|
||||
// Set custom values
|
||||
textConfigState.fontSize = 18;
|
||||
textConfigState.fontFamily = "Georgia";
|
||||
textConfigState.color = "#00ff00" as HexColor;
|
||||
textConfigState.align = TextAlign.RIGHT;
|
||||
textConfigState.paddingX = 5;
|
||||
textConfigState.offsetY = 15;
|
||||
|
||||
// Get serialized data
|
||||
const data = textConfigState[STATE_DATA];
|
||||
expect(data).toEqual({
|
||||
fontSize: 18,
|
||||
fontFamily: "Georgia",
|
||||
color: "#00ff00",
|
||||
align: TextAlign.RIGHT,
|
||||
paddingX: 5,
|
||||
offsetY: 15,
|
||||
});
|
||||
|
||||
// Restore from serialized data
|
||||
textConfigState[STATE_DATA] = {
|
||||
fontSize: 24,
|
||||
fontFamily: "Arial",
|
||||
color: "#ffffff" as HexColor,
|
||||
align: TextAlign.CENTER,
|
||||
paddingX: 10,
|
||||
offsetY: 0,
|
||||
};
|
||||
|
||||
expect(textConfigState.fontSize).toBe(24);
|
||||
expect(textConfigState.fontFamily).toBe("Arial");
|
||||
expect(textConfigState.color).toBe("#ffffff");
|
||||
expect(textConfigState.align).toBe(TextAlign.CENTER);
|
||||
expect(textConfigState.paddingX).toBe(10);
|
||||
expect(textConfigState.offsetY).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,95 +1,95 @@
|
||||
import { STATE_DATA } from "$states/persisted.svelte";
|
||||
import { createState } from "$states/texts.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("texts.svelte", () => {
|
||||
describe("addText", () => {
|
||||
it("should add new text with unique ID", () => {
|
||||
const state = createState();
|
||||
const initialLength = state.texts.length;
|
||||
|
||||
state.addText("Test text");
|
||||
|
||||
expect(state.texts).toHaveLength(initialLength + 1);
|
||||
expect(state.texts[initialLength].text).toBe("Test text");
|
||||
expect(state.texts[initialLength].id).toBeDefined();
|
||||
});
|
||||
|
||||
it("should not add empty text", () => {
|
||||
const state = createState();
|
||||
const initialLength = state.texts.length;
|
||||
|
||||
state.addText("");
|
||||
state.addText(" ");
|
||||
|
||||
expect(state.texts).toHaveLength(initialLength);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeText", () => {
|
||||
it("should remove text by ID", () => {
|
||||
const state = createState();
|
||||
const idToRemove = state.texts[0].id;
|
||||
const initialLength = state.texts.length;
|
||||
|
||||
state.removeText(idToRemove);
|
||||
|
||||
expect(state.texts).toHaveLength(initialLength - 1);
|
||||
expect(state.texts.find((item) => item.id === idToRemove)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not affect other texts when removing", () => {
|
||||
const state = createState();
|
||||
const remainingTexts = state.texts.filter((item) => item.id !== state.texts[0].id);
|
||||
const idToRemove = state.texts[0].id;
|
||||
|
||||
state.removeText(idToRemove);
|
||||
|
||||
expect(state.texts).toStrictEqual(remainingTexts);
|
||||
});
|
||||
});
|
||||
|
||||
describe("clear", () => {
|
||||
it("should remove all texts", () => {
|
||||
const state = createState();
|
||||
state.addText("Test 1");
|
||||
state.addText("Test 2");
|
||||
state.addText("Test 3");
|
||||
|
||||
state.clear();
|
||||
|
||||
expect(state.texts).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("STATE_DATA", () => {
|
||||
it("should serialize and deserialize texts", () => {
|
||||
const state = createState();
|
||||
state.clear();
|
||||
state.addText("First");
|
||||
state.addText("Second");
|
||||
state.addText("Third");
|
||||
|
||||
const data = state[STATE_DATA];
|
||||
expect(data).toEqual(["First", "Second", "Third"]);
|
||||
|
||||
// Restore from serialized data
|
||||
state[STATE_DATA] = ["New 1", "New 2"];
|
||||
|
||||
expect(state.texts).toHaveLength(2);
|
||||
expect(state.texts[0].text).toBe("New 1");
|
||||
expect(state.texts[1].text).toBe("New 2");
|
||||
expect(state.texts[0].id).toBe(0);
|
||||
expect(state.texts[1].id).toBe(1);
|
||||
});
|
||||
|
||||
it("should handle empty array", () => {
|
||||
const state = createState();
|
||||
state.addText("Test");
|
||||
|
||||
state[STATE_DATA] = [];
|
||||
|
||||
expect(state.texts).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
import { STATE_DATA } from "$states/persisted.svelte";
|
||||
import { createState } from "$states/texts.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("texts.svelte", () => {
|
||||
describe("addText", () => {
|
||||
it("should add new text with unique ID", () => {
|
||||
const state = createState();
|
||||
const initialLength = state.texts.length;
|
||||
|
||||
state.addText("Test text");
|
||||
|
||||
expect(state.texts).toHaveLength(initialLength + 1);
|
||||
expect(state.texts[initialLength].text).toBe("Test text");
|
||||
expect(state.texts[initialLength].id).toBeDefined();
|
||||
});
|
||||
|
||||
it("should not add empty text", () => {
|
||||
const state = createState();
|
||||
const initialLength = state.texts.length;
|
||||
|
||||
state.addText("");
|
||||
state.addText(" ");
|
||||
|
||||
expect(state.texts).toHaveLength(initialLength);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeText", () => {
|
||||
it("should remove text by ID", () => {
|
||||
const state = createState();
|
||||
const idToRemove = state.texts[0].id;
|
||||
const initialLength = state.texts.length;
|
||||
|
||||
state.removeText(idToRemove);
|
||||
|
||||
expect(state.texts).toHaveLength(initialLength - 1);
|
||||
expect(state.texts.find((item) => item.id === idToRemove)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not affect other texts when removing", () => {
|
||||
const state = createState();
|
||||
const remainingTexts = state.texts.filter((item) => item.id !== state.texts[0].id);
|
||||
const idToRemove = state.texts[0].id;
|
||||
|
||||
state.removeText(idToRemove);
|
||||
|
||||
expect(state.texts).toStrictEqual(remainingTexts);
|
||||
});
|
||||
});
|
||||
|
||||
describe("clear", () => {
|
||||
it("should remove all texts", () => {
|
||||
const state = createState();
|
||||
state.addText("Test 1");
|
||||
state.addText("Test 2");
|
||||
state.addText("Test 3");
|
||||
|
||||
state.clear();
|
||||
|
||||
expect(state.texts).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("STATE_DATA", () => {
|
||||
it("should serialize and deserialize texts", () => {
|
||||
const state = createState();
|
||||
state.clear();
|
||||
state.addText("First");
|
||||
state.addText("Second");
|
||||
state.addText("Third");
|
||||
|
||||
const data = state[STATE_DATA];
|
||||
expect(data).toEqual(["First", "Second", "Third"]);
|
||||
|
||||
// Restore from serialized data
|
||||
state[STATE_DATA] = ["New 1", "New 2"];
|
||||
|
||||
expect(state.texts).toHaveLength(2);
|
||||
expect(state.texts[0].text).toBe("New 1");
|
||||
expect(state.texts[1].text).toBe("New 2");
|
||||
expect(state.texts[0].id).toBe(0);
|
||||
expect(state.texts[1].id).toBe(1);
|
||||
});
|
||||
|
||||
it("should handle empty array", () => {
|
||||
const state = createState();
|
||||
state.addText("Test");
|
||||
|
||||
state[STATE_DATA] = [];
|
||||
|
||||
expect(state.texts).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
import { Theme } from "$lib/constants";
|
||||
import { STATE_DATA } from "$states/persisted.svelte";
|
||||
import { themeState } from "$states/theme.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const THEME_VALUES = Object.values(Theme);
|
||||
|
||||
describe("theme.svelte", () => {
|
||||
describe("initial state", () => {
|
||||
it("should have valid initial theme value", () => {
|
||||
expect(THEME_VALUES).toContain(themeState.current);
|
||||
});
|
||||
});
|
||||
|
||||
describe("toggle", () => {
|
||||
it("should toggle between themes", () => {
|
||||
themeState.current = Theme.LIGHT;
|
||||
themeState.toggle();
|
||||
expect(themeState.current).toBe(Theme.DARK);
|
||||
|
||||
themeState.toggle();
|
||||
expect(themeState.current).toBe(Theme.LIGHT);
|
||||
});
|
||||
});
|
||||
|
||||
describe("current setter", () => {
|
||||
it("should set theme correctly", () => {
|
||||
themeState.current = Theme.DARK;
|
||||
expect(themeState.current).toBe(Theme.DARK);
|
||||
|
||||
themeState.current = Theme.LIGHT;
|
||||
expect(themeState.current).toBe(Theme.LIGHT);
|
||||
});
|
||||
});
|
||||
|
||||
describe("STATE_DATA", () => {
|
||||
it("should serialize and deserialize theme", () => {
|
||||
themeState.current = Theme.DARK;
|
||||
expect(themeState[STATE_DATA]).toEqual({ current: Theme.DARK });
|
||||
|
||||
themeState[STATE_DATA] = { current: Theme.LIGHT };
|
||||
expect(themeState.current).toBe(Theme.LIGHT);
|
||||
});
|
||||
});
|
||||
});
|
||||
import { Theme } from "$lib/constants";
|
||||
import { STATE_DATA } from "$states/persisted.svelte";
|
||||
import { themeState } from "$states/theme.svelte";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const THEME_VALUES = Object.values(Theme);
|
||||
|
||||
describe("theme.svelte", () => {
|
||||
describe("initial state", () => {
|
||||
it("should have valid initial theme value", () => {
|
||||
expect(THEME_VALUES).toContain(themeState.current);
|
||||
});
|
||||
});
|
||||
|
||||
describe("toggle", () => {
|
||||
it("should toggle between themes", () => {
|
||||
themeState.current = Theme.LIGHT;
|
||||
themeState.toggle();
|
||||
expect(themeState.current).toBe(Theme.DARK);
|
||||
|
||||
themeState.toggle();
|
||||
expect(themeState.current).toBe(Theme.LIGHT);
|
||||
});
|
||||
});
|
||||
|
||||
describe("current setter", () => {
|
||||
it("should set theme correctly", () => {
|
||||
themeState.current = Theme.DARK;
|
||||
expect(themeState.current).toBe(Theme.DARK);
|
||||
|
||||
themeState.current = Theme.LIGHT;
|
||||
expect(themeState.current).toBe(Theme.LIGHT);
|
||||
});
|
||||
});
|
||||
|
||||
describe("STATE_DATA", () => {
|
||||
it("should serialize and deserialize theme", () => {
|
||||
themeState.current = Theme.DARK;
|
||||
expect(themeState[STATE_DATA]).toEqual({ current: Theme.DARK });
|
||||
|
||||
themeState[STATE_DATA] = { current: Theme.LIGHT };
|
||||
expect(themeState.current).toBe(Theme.LIGHT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user