chore: update eslint config, update tests, constants
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { includeIgnoreFile } from "@eslint/compat";
|
import { includeIgnoreFile } from "@eslint/compat";
|
||||||
import js from "@eslint/js";
|
import js from "@eslint/js";
|
||||||
|
import vitest from "@vitest/eslint-plugin";
|
||||||
import prettier from "eslint-config-prettier";
|
import prettier from "eslint-config-prettier";
|
||||||
import svelte from "eslint-plugin-svelte";
|
import svelte from "eslint-plugin-svelte";
|
||||||
import { defineConfig } from "eslint/config";
|
import { defineConfig } from "eslint/config";
|
||||||
@@ -26,6 +27,14 @@ export default defineConfig(
|
|||||||
"no-undef": "off",
|
"no-undef": "off",
|
||||||
"no-unused-vars": "off",
|
"no-unused-vars": "off",
|
||||||
"no-console": ["error", { allow: ["warn", "error"] }],
|
"no-console": ["error", { allow: ["warn", "error"] }],
|
||||||
|
"@typescript-eslint/no-magic-numbers": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
enforceConst: true,
|
||||||
|
ignoreDefaultValues: true,
|
||||||
|
ignore: [0, 1, 2],
|
||||||
|
},
|
||||||
|
],
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
@@ -47,4 +56,45 @@ export default defineConfig(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
files: ["**/constants.ts", "**/constants/*.ts"],
|
||||||
|
rules: {
|
||||||
|
"no-magic-numbers": "off",
|
||||||
|
"@typescript-eslint/no-magic-numbers": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/constants.ts", "**/constants/*.ts"],
|
||||||
|
rules: {
|
||||||
|
"no-magic-numbers": "off",
|
||||||
|
"@typescript-eslint/no-magic-numbers": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["scripts/**"],
|
||||||
|
rules: {
|
||||||
|
"no-magic-numbers": "off",
|
||||||
|
"@typescript-eslint/no-magic-numbers": "off",
|
||||||
|
"no-console": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.test.ts", "**/*.spec.ts", "tests/**/*"],
|
||||||
|
plugins: { vitest },
|
||||||
|
rules: {
|
||||||
|
...vitest.configs.recommended.rules,
|
||||||
|
|
||||||
|
"vitest/no-focused-tests": "error",
|
||||||
|
"vitest/no-disabled-tests": "warn",
|
||||||
|
"vitest/expect-expect": "error",
|
||||||
|
"vitest/no-conditional-expect": "error",
|
||||||
|
"vitest/require-hook": "warn",
|
||||||
|
"vitest/consistent-test-it": ["error", { fn: "it", withinDescribe: "it" }],
|
||||||
|
"vitest/require-top-level-describe": "error",
|
||||||
|
"vitest/no-identical-title": "error",
|
||||||
|
|
||||||
|
"no-magic-numbers": "off",
|
||||||
|
"@typescript-eslint/no-magic-numbers": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Generated
+28
@@ -31,6 +31,7 @@
|
|||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@vitest/coverage-istanbul": "^4.0.18",
|
"@vitest/coverage-istanbul": "^4.0.18",
|
||||||
|
"@vitest/eslint-plugin": "^1.6.9",
|
||||||
"@vitest/ui": "^4.0.18",
|
"@vitest/ui": "^4.0.18",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
@@ -2632,6 +2633,33 @@
|
|||||||
"vitest": "4.0.18"
|
"vitest": "4.0.18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vitest/eslint-plugin": {
|
||||||
|
"version": "1.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.6.9.tgz",
|
||||||
|
"integrity": "sha512-9WfPx1OwJ19QLCSRLkqVO7//1WcWnK3fE/3fJhKMAmDe8+9G4rB47xCNIIeCq3FdEzkIoLTfDlwDlPBaUTMhow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/scope-manager": "^8.55.0",
|
||||||
|
"@typescript-eslint/utils": "^8.55.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=8.57.0",
|
||||||
|
"typescript": ">=5.0.0",
|
||||||
|
"vitest": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vitest": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "4.0.18",
|
"version": "4.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@vitest/coverage-istanbul": "^4.0.18",
|
"@vitest/coverage-istanbul": "^4.0.18",
|
||||||
|
"@vitest/eslint-plugin": "^1.6.9",
|
||||||
"@vitest/ui": "^4.0.18",
|
"@vitest/ui": "^4.0.18",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PANEL_SETTINGS } from "$lib/constants";
|
import { PANEL_SETTINGS } from "$lib/constants";
|
||||||
import { imageConfigState } from "$states/imageConfig.svelte";
|
import { imageState } from "$states/image.svelte";
|
||||||
import { textConfigState } from "$states/textConfig.svelte";
|
import { textConfigState } from "$states/textConfig.svelte";
|
||||||
import { Image, Layer, Stage, Text } from "svelte-konva";
|
import { Image, Layer, Stage, Text } from "svelte-konva";
|
||||||
|
|
||||||
@@ -12,14 +12,18 @@
|
|||||||
let { text, stage = $bindable() }: Props = $props();
|
let { text, stage = $bindable() }: Props = $props();
|
||||||
|
|
||||||
let x = $derived(textConfigState.paddingX);
|
let x = $derived(textConfigState.paddingX);
|
||||||
let y = $derived(10 + textConfigState.offsetY);
|
let y = $derived(textConfigState.offsetY);
|
||||||
let width = $derived(PANEL_SETTINGS.PANEL_WIDTH - 2 * textConfigState.paddingX);
|
let width = $derived(PANEL_SETTINGS.PANEL_WIDTH - 2 * textConfigState.paddingX);
|
||||||
let height = PANEL_SETTINGS.PANEL_HEIGHT_DEFAULT;
|
let height = PANEL_SETTINGS.PANEL_HEIGHT_DEFAULT;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Stage width={320} height={100} bind:this={stage}>
|
<Stage
|
||||||
|
width={PANEL_SETTINGS.PANEL_WIDTH}
|
||||||
|
height={PANEL_SETTINGS.PANEL_HEIGHT_DEFAULT}
|
||||||
|
bind:this={stage}
|
||||||
|
>
|
||||||
<Layer>
|
<Layer>
|
||||||
<Image image={imageConfigState.image}></Image>
|
<Image image={imageState.croppedImage}></Image>
|
||||||
<Text
|
<Text
|
||||||
{text}
|
{text}
|
||||||
{x}
|
{x}
|
||||||
|
|||||||
@@ -6,13 +6,19 @@
|
|||||||
import ColorPicker from "$components/ui/ColorPicker.svelte";
|
import ColorPicker from "$components/ui/ColorPicker.svelte";
|
||||||
import RangeSlider from "$components/ui/RangeSlider.svelte";
|
import RangeSlider from "$components/ui/RangeSlider.svelte";
|
||||||
import SelectFont from "$components/ui/SelectFont.svelte";
|
import SelectFont from "$components/ui/SelectFont.svelte";
|
||||||
|
import { TYPOGRAPHY } from "$lib/constants";
|
||||||
import { textConfigState } from "$states/textConfig.svelte";
|
import { textConfigState } from "$states/textConfig.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card title="Настройки текста">
|
<Card title="Настройки текста">
|
||||||
<SettingsGrid>
|
<SettingsGrid>
|
||||||
<SettingsRow label="Размер">
|
<SettingsRow label="Размер">
|
||||||
<RangeSlider bind:value={textConfigState.fontSize} min={10} max={100} step={1} />
|
<RangeSlider
|
||||||
|
bind:value={textConfigState.fontSize}
|
||||||
|
min={TYPOGRAPHY.FONT_SIZE_MIN}
|
||||||
|
max={TYPOGRAPHY.FONT_SIZE_MAX}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
<SettingsRow label="Шрифт">
|
<SettingsRow label="Шрифт">
|
||||||
<SelectFont bind:value={textConfigState.fontFamily} />
|
<SelectFont bind:value={textConfigState.fontFamily} />
|
||||||
@@ -24,10 +30,20 @@
|
|||||||
<Alignment bind:align={textConfigState.align} />
|
<Alignment bind:align={textConfigState.align} />
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
<SettingsRow label="Отступы">
|
<SettingsRow label="Отступы">
|
||||||
<RangeSlider bind:value={textConfigState.paddingX} min={0} max={100} step={1} />
|
<RangeSlider
|
||||||
|
bind:value={textConfigState.paddingX}
|
||||||
|
min={TYPOGRAPHY.PADDING_X_MIN}
|
||||||
|
max={TYPOGRAPHY.PADDING_X_MAX}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
<SettingsRow label="Смещение">
|
<SettingsRow label="Смещение">
|
||||||
<RangeSlider bind:value={textConfigState.offsetY} min={-100} max={100} step={1} />
|
<RangeSlider
|
||||||
|
bind:value={textConfigState.offsetY}
|
||||||
|
min={TYPOGRAPHY.VERTICAL_OFFSET_MIN}
|
||||||
|
max={TYPOGRAPHY.VERTICAL_OFFSET_MAX}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
</SettingsGrid>
|
</SettingsGrid>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
let { value = $bindable() }: Props = $props();
|
let { value = $bindable() }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input type="color" bind:value class="color-input" />
|
<input type="color" role="button" bind:value class="color-input" />
|
||||||
<span class="color-value">{value}</span>
|
<span class="color-value">{value}</span>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
|
|
||||||
export const STATE_DATA = Symbol("state-data");
|
export const STATE_DATA = Symbol("state-data");
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||||
export const DEBOUNCE_DURATION = import.meta.env.MODE === "test" ? 0 : 500;
|
export const DEBOUNCE_DURATION = import.meta.env.MODE === "test" ? 0 : 500;
|
||||||
|
|
||||||
export interface Persistable<D> {
|
export interface Persistable<D> {
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ exports[`Application Constants Logic > Global Contract (Snapshot) > should match
|
|||||||
{
|
{
|
||||||
"DEFAULT_TEXT_ALIGN": "center",
|
"DEFAULT_TEXT_ALIGN": "center",
|
||||||
"IMAGE_SETTINGS": {
|
"IMAGE_SETTINGS": {
|
||||||
|
"BRIGHTNESS_MAX": 100,
|
||||||
|
"BRIGHTNESS_MIN": 0,
|
||||||
|
"CONTRAST_MAX": 150,
|
||||||
|
"CONTRAST_MIN": 50,
|
||||||
|
"DEFAULT_BACKGROUND_IMAGE": "./backgrounds/b1.jpg",
|
||||||
|
"DEFAULT_BRIGHTNESS": 100,
|
||||||
|
"DEFAULT_CONTRAST": 100,
|
||||||
"MAX_FILE_SIZE": 10485760,
|
"MAX_FILE_SIZE": 10485760,
|
||||||
"SUPPORTED_FORMATS": [
|
"SUPPORTED_FORMATS": [
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
@@ -14,7 +21,6 @@ exports[`Application Constants Logic > Global Contract (Snapshot) > should match
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
"PANEL_SETTINGS": {
|
"PANEL_SETTINGS": {
|
||||||
"DEFAULT_BACKGROUND_IMAGE": "./backgrounds/b1.jpg",
|
|
||||||
"PANEL_HEIGHT_DEFAULT": 100,
|
"PANEL_HEIGHT_DEFAULT": 100,
|
||||||
"PANEL_HEIGHT_MAX": 200,
|
"PANEL_HEIGHT_MAX": 200,
|
||||||
"PANEL_WIDTH": 320,
|
"PANEL_WIDTH": 320,
|
||||||
@@ -42,6 +48,7 @@ exports[`Application Constants Logic > Global Contract (Snapshot) > should match
|
|||||||
"MAX_TEXT_LENGTH": 100,
|
"MAX_TEXT_LENGTH": 100,
|
||||||
"PADDING_X_DEFAULT": 10,
|
"PADDING_X_DEFAULT": 10,
|
||||||
"PADDING_X_MAX": 100,
|
"PADDING_X_MAX": 100,
|
||||||
|
"PADDING_X_MIN": 0,
|
||||||
"TEXT_COLOR_DEFAULT": "#ffffff",
|
"TEXT_COLOR_DEFAULT": "#ffffff",
|
||||||
"VERTICAL_OFFSET_MAX": 100,
|
"VERTICAL_OFFSET_MAX": 100,
|
||||||
"VERTICAL_OFFSET_MIN": -100,
|
"VERTICAL_OFFSET_MIN": -100,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import ImageManager from "$components/image/ImageManager.svelte";
|
import ImageManager from "$components/image/ImageManager.svelte";
|
||||||
import { render } from "@testing-library/svelte";
|
import { render } from "@testing-library/svelte";
|
||||||
import { describe, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("ImageManager.svelte", () => {
|
describe("ImageManager.svelte", () => {
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
render(ImageManager);
|
const { container } = render(ImageManager);
|
||||||
|
|
||||||
|
expect(container).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import Preview from "$components/panel/Preview.svelte";
|
|||||||
import { cleanup, render } from "@testing-library/svelte";
|
import { cleanup, render } from "@testing-library/svelte";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Preview.svelte", () => {
|
describe("Preview.svelte", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
const { container } = render(Preview, {
|
const { container } = render(Preview, {
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import PreviewAll from "$components/panel/PreviewAll.svelte";
|
import PreviewAll from "$components/panel/PreviewAll.svelte";
|
||||||
import { textsState } from "$states/texts.svelte";
|
|
||||||
import { render } from "@testing-library/svelte";
|
import { render } from "@testing-library/svelte";
|
||||||
import { beforeEach, describe, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("PreviewAll.svelte", () => {
|
describe("PreviewAll.svelte", () => {
|
||||||
beforeEach(() => {
|
|
||||||
textsState.texts.length = 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
render(PreviewAll);
|
const { container } = render(PreviewAll);
|
||||||
|
|
||||||
|
expect(container).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import TextConfig from "$components/text/TextConfig.svelte";
|
import TextConfig from "$components/text/TextConfig.svelte";
|
||||||
import { cleanup, render } from "@testing-library/svelte";
|
import { render } from "@testing-library/svelte";
|
||||||
import { afterEach, describe, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("TextConfig.svelte", () => {
|
describe("TextConfig.svelte", () => {
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
render(TextConfig);
|
const { container } = render(TextConfig);
|
||||||
|
|
||||||
|
expect(container).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import TextInlineEdit from "$components/text/TextInlineEdit.svelte";
|
|||||||
import { cleanup, render, screen } from "@testing-library/svelte";
|
import { cleanup, render, screen } from "@testing-library/svelte";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("TextInlineEdit.svelte", () => {
|
describe("TextInlineEdit.svelte", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
it("should render with initial text", () => {
|
it("should render with initial text", () => {
|
||||||
render(TextInlineEdit, {
|
render(TextInlineEdit, {
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ describe("Badge.svelte", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should render with empty string", () => {
|
it("should render with empty string", () => {
|
||||||
render(Badge, {
|
const { container } = render(Badge, {
|
||||||
props: {
|
props: {
|
||||||
text: "",
|
text: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(container.textContent?.trim()).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render with zero", () => {
|
it("should render with zero", () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import ColorPicker from "$components/ui/ColorPicker.svelte";
|
import ColorPicker from "$components/ui/ColorPicker.svelte";
|
||||||
import { render } from "@testing-library/svelte";
|
import { render, screen } from "@testing-library/svelte";
|
||||||
import { describe, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("ColorPicker.svelte", () => {
|
describe("ColorPicker.svelte", () => {
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
@@ -9,5 +9,6 @@ describe("ColorPicker.svelte", () => {
|
|||||||
value: "#ffffff",
|
value: "#ffffff",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
expect(screen.getByRole("button")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import SelectFont from "$components/ui/SelectFont.svelte";
|
import SelectFont from "$components/ui/SelectFont.svelte";
|
||||||
import { render } from "@testing-library/svelte";
|
import { render, screen } from "@testing-library/svelte";
|
||||||
import { describe, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("SelectFont.svelte", () => {
|
describe("SelectFont.svelte", () => {
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
@@ -9,5 +9,6 @@ describe("SelectFont.svelte", () => {
|
|||||||
value: "Arial",
|
value: "Arial",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
expect(screen.getByRole("combobox")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ describe("Application Constants Logic", () => {
|
|||||||
|
|
||||||
describe("Assets Existence", () => {
|
describe("Assets Existence", () => {
|
||||||
it("should verify that the default background image exists", () => {
|
it("should verify that the default background image exists", () => {
|
||||||
const { DEFAULT_BACKGROUND_IMAGE } = Constants.PANEL_SETTINGS;
|
const { DEFAULT_BACKGROUND_IMAGE } = Constants.IMAGE_SETTINGS;
|
||||||
|
|
||||||
const relativePath = DEFAULT_BACKGROUND_IMAGE.replace(/^\.\//, "");
|
const relativePath = DEFAULT_BACKGROUND_IMAGE.replace(/^\.\//, "");
|
||||||
const fullPath = path.resolve(process.cwd(), "static", relativePath);
|
const fullPath = path.resolve(process.cwd(), "static", relativePath);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { DownloadService, type DownloadItem } from "$services/downloadService";
|
|||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import JSZip from "jszip";
|
import JSZip from "jszip";
|
||||||
import type { Stage } from "konva/lib/Stage";
|
import type { Stage } from "konva/lib/Stage";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { assert, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
vi.mock("file-saver", () => {
|
vi.mock("file-saver", () => {
|
||||||
return {
|
return {
|
||||||
@@ -58,10 +58,9 @@ describe("DownloadService", () => {
|
|||||||
"test-panel-1.png",
|
"test-panel-1.png",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.success).toBe(false);
|
assert(result.success === false);
|
||||||
if (!result.success) {
|
|
||||||
expect(result.error).toContain("Konva Stage не найден или не поддерживает toBlob");
|
expect(result.error).toContain("Konva Stage не найден или не поддерживает toBlob");
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle blob creation failure", async () => {
|
it("should handle blob creation failure", async () => {
|
||||||
@@ -71,10 +70,9 @@ describe("DownloadService", () => {
|
|||||||
|
|
||||||
const result = await service.downloadPanel(mockKonvaStage, "test-panel-1.png");
|
const result = await service.downloadPanel(mockKonvaStage, "test-panel-1.png");
|
||||||
|
|
||||||
expect(result.success).toBe(false);
|
assert(result.success === false);
|
||||||
if (!result.success) {
|
|
||||||
expect(result.error).toContain("Не удалось создать изображение");
|
expect(result.error).toContain("Не удалось создать изображение");
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,10 +120,9 @@ describe("DownloadService", () => {
|
|||||||
];
|
];
|
||||||
const result = await service.downloadAll(panels);
|
const result = await service.downloadAll(panels);
|
||||||
|
|
||||||
expect(result.success).toBe(false);
|
assert(result.success === false);
|
||||||
if (!result.success) {
|
|
||||||
expect(result.error).toContain("Ошибка сохранения архива");
|
expect(result.error).toContain("Ошибка сохранения архива");
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { PANEL_SETTINGS } from "$lib/constants";
|
import { PANEL_SETTINGS } from "$lib/constants";
|
||||||
import { imageConfigState, ImageConfigState } from "$states/imageConfig.svelte";
|
import { imageState } from "$states/image.svelte";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
type ImageEventHandler = ((this: HTMLImageElement, ev?: Event) => void) | null;
|
type ImageEventHandler = ((this: HTMLImageElement, ev?: Event) => void) | null;
|
||||||
@@ -61,15 +61,15 @@ vi.stubGlobal(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
describe("ImageConfigState", () => {
|
describe("imageState", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
lastOnload = null;
|
lastOnload = null;
|
||||||
lastOnerror = null;
|
lastOnerror = null;
|
||||||
imageConfigState.reset();
|
imageState.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create new instance with default values", () => {
|
it("should create new instance with default values", () => {
|
||||||
const newState = new ImageConfigState();
|
const newState = new imageState();
|
||||||
expect(newState.image).toBeUndefined();
|
expect(newState.image).toBeUndefined();
|
||||||
expect(newState.imageLink).toBe("");
|
expect(newState.imageLink).toBe("");
|
||||||
expect(newState.imageReady).toBe(false);
|
expect(newState.imageReady).toBe(false);
|
||||||
@@ -81,136 +81,136 @@ describe("ImageConfigState", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should initialize with default background image", async () => {
|
it("should initialize with default background image", async () => {
|
||||||
await imageConfigState.uploadImageByLink(PANEL_SETTINGS.DEFAULT_BACKGROUND_IMAGE);
|
await imageState.uploadImageByLink(PANEL_SETTINGS.DEFAULT_BACKGROUND_IMAGE);
|
||||||
|
|
||||||
expect(imageConfigState.imageReady).toBe(true);
|
expect(imageState.imageReady).toBe(true);
|
||||||
expect(imageConfigState.image).toBeDefined();
|
expect(imageState.image).toBeDefined();
|
||||||
expect(imageConfigState.imageLink).toBe(PANEL_SETTINGS.DEFAULT_BACKGROUND_IMAGE);
|
expect(imageState.imageLink).toBe(PANEL_SETTINGS.DEFAULT_BACKGROUND_IMAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle manual image upload correctly", async () => {
|
it("should handle manual image upload correctly", async () => {
|
||||||
const testLink = "https://example.com/test.png";
|
const testLink = "https://example.com/test.png";
|
||||||
const uploadPromise = imageConfigState.uploadImageByLink(testLink);
|
const uploadPromise = imageState.uploadImageByLink(testLink);
|
||||||
|
|
||||||
expect(imageConfigState.imageReady).toBe(false);
|
expect(imageState.imageReady).toBe(false);
|
||||||
|
|
||||||
await uploadPromise;
|
await uploadPromise;
|
||||||
expect(imageConfigState.imageReady).toBe(true);
|
expect(imageState.imageReady).toBe(true);
|
||||||
expect(imageConfigState.imageLink).toBe(testLink);
|
expect(imageState.imageLink).toBe(testLink);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should reset state to defaults", async () => {
|
it("should reset state to defaults", async () => {
|
||||||
await imageConfigState.uploadImageByLink("some-image.png");
|
await imageState.uploadImageByLink("some-image.png");
|
||||||
imageConfigState.cropLeft = 100;
|
imageState.cropLeft = 100;
|
||||||
|
|
||||||
imageConfigState.reset();
|
imageState.reset();
|
||||||
|
|
||||||
expect(imageConfigState.imageReady).toBe(false);
|
expect(imageState.imageReady).toBe(false);
|
||||||
expect(imageConfigState.imageLink).toBe("");
|
expect(imageState.imageLink).toBe("");
|
||||||
expect(imageConfigState.cropLeft).toBe(0);
|
expect(imageState.cropLeft).toBe(0);
|
||||||
expect(imageConfigState.image).toBeUndefined();
|
expect(imageState.image).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle image loading error", async () => {
|
it("should handle image loading error", async () => {
|
||||||
await expect(imageConfigState.uploadImageByLink("error-link")).rejects.toThrow(
|
await expect(imageState.uploadImageByLink("error-link")).rejects.toThrow(
|
||||||
"Failed to load image",
|
"Failed to load image",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(imageConfigState.imageReady).toBe(false);
|
expect(imageState.imageReady).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should abort previous upload when new upload starts", async () => {
|
it("should abort previous upload when new upload starts", async () => {
|
||||||
const upload1 = imageConfigState.uploadImageByLink("test1.jpg");
|
const upload1 = imageState.uploadImageByLink("test1.jpg");
|
||||||
const upload2 = imageConfigState.uploadImageByLink("test2.jpg");
|
const upload2 = imageState.uploadImageByLink("test2.jpg");
|
||||||
|
|
||||||
await expect(upload1).rejects.toThrow("Aborted");
|
await expect(upload1).rejects.toThrow("Aborted");
|
||||||
await expect(upload2).resolves.toBeUndefined();
|
await expect(upload2).resolves.toBeUndefined();
|
||||||
|
|
||||||
expect(imageConfigState.imageLink).toBe("test2.jpg");
|
expect(imageState.imageLink).toBe("test2.jpg");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cleanup previous image before loading new one", async () => {
|
it("should cleanup previous image before loading new one", async () => {
|
||||||
await imageConfigState.uploadImageByLink("test1.jpg");
|
await imageState.uploadImageByLink("test1.jpg");
|
||||||
const firstImage = imageConfigState.image;
|
const firstImage = imageState.image;
|
||||||
|
|
||||||
await imageConfigState.uploadImageByLink("test2.jpg");
|
await imageState.uploadImageByLink("test2.jpg");
|
||||||
|
|
||||||
expect(firstImage?.onload).toBeNull();
|
expect(firstImage?.onload).toBeNull();
|
||||||
expect(firstImage?.onerror).toBeNull();
|
expect(firstImage?.onerror).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set crop values", () => {
|
it("should set crop values", () => {
|
||||||
imageConfigState.cropLeft = 10;
|
imageState.cropLeft = 10;
|
||||||
imageConfigState.cropTop = 20;
|
imageState.cropTop = 20;
|
||||||
imageConfigState.cropRight = 30;
|
imageState.cropRight = 30;
|
||||||
imageConfigState.cropBottom = 40;
|
imageState.cropBottom = 40;
|
||||||
|
|
||||||
expect(imageConfigState.cropLeft).toBe(10);
|
expect(imageState.cropLeft).toBe(10);
|
||||||
expect(imageConfigState.cropTop).toBe(20);
|
expect(imageState.cropTop).toBe(20);
|
||||||
expect(imageConfigState.cropRight).toBe(30);
|
expect(imageState.cropRight).toBe(30);
|
||||||
expect(imageConfigState.cropBottom).toBe(40);
|
expect(imageState.cropBottom).toBe(40);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cleanup image event handlers on reset", async () => {
|
it("should cleanup image event handlers on reset", async () => {
|
||||||
await imageConfigState.uploadImageByLink("test.jpg");
|
await imageState.uploadImageByLink("test.jpg");
|
||||||
const img = imageConfigState.image;
|
const img = imageState.image;
|
||||||
|
|
||||||
imageConfigState.reset();
|
imageState.reset();
|
||||||
|
|
||||||
expect(img?.onload).toBeNull();
|
expect(img?.onload).toBeNull();
|
||||||
expect(img?.onerror).toBeNull();
|
expect(img?.onerror).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should abort ongoing upload on reset", async () => {
|
it("should abort ongoing upload on reset", async () => {
|
||||||
const upload = imageConfigState.uploadImageByLink("test.jpg");
|
const upload = imageState.uploadImageByLink("test.jpg");
|
||||||
|
|
||||||
imageConfigState.reset();
|
imageState.reset();
|
||||||
|
|
||||||
await expect(upload).rejects.toThrow("Aborted");
|
await expect(upload).rejects.toThrow("Aborted");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cleanup resources on destroy", async () => {
|
it("should cleanup resources on destroy", async () => {
|
||||||
await imageConfigState.uploadImageByLink("test.jpg");
|
await imageState.uploadImageByLink("test.jpg");
|
||||||
const img = imageConfigState.image;
|
const img = imageState.image;
|
||||||
|
|
||||||
imageConfigState.destroy();
|
imageState.destroy();
|
||||||
|
|
||||||
expect(imageConfigState.image).toBeUndefined();
|
expect(imageState.image).toBeUndefined();
|
||||||
expect(img?.onload).toBeNull();
|
expect(img?.onload).toBeNull();
|
||||||
expect(img?.onerror).toBeNull();
|
expect(img?.onerror).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should abort ongoing upload on destroy", async () => {
|
it("should abort ongoing upload on destroy", async () => {
|
||||||
const upload = imageConfigState.uploadImageByLink("test.jpg");
|
const upload = imageState.uploadImageByLink("test.jpg");
|
||||||
|
|
||||||
imageConfigState.destroy();
|
imageState.destroy();
|
||||||
|
|
||||||
await expect(upload).rejects.toThrow("Aborted");
|
await expect(upload).rejects.toThrow("Aborted");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cover aborted onload branch", async () => {
|
it("should cover aborted onload branch", async () => {
|
||||||
const promise = imageConfigState.uploadImageByLink("test.png");
|
const promise = imageState.uploadImageByLink("test.png");
|
||||||
|
|
||||||
imageConfigState.destroy();
|
imageState.destroy();
|
||||||
|
|
||||||
if (lastOnload) {
|
if (lastOnload) {
|
||||||
lastOnload.call(new Image() as HTMLImageElement, new Event("load"));
|
lastOnload.call(new Image() as HTMLImageElement, new Event("load"));
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(promise).rejects.toThrow();
|
await expect(promise).rejects.toThrow();
|
||||||
expect(imageConfigState.imageReady).toBe(false);
|
expect(imageState.imageReady).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cover aborted onerror branch", async () => {
|
it("should cover aborted onerror branch", async () => {
|
||||||
const promise = imageConfigState.uploadImageByLink("test.png");
|
const promise = imageState.uploadImageByLink("test.png");
|
||||||
|
|
||||||
imageConfigState.destroy();
|
imageState.destroy();
|
||||||
|
|
||||||
if (lastOnerror) {
|
if (lastOnerror) {
|
||||||
lastOnerror.call(new Image() as HTMLImageElement, new Event("load"));
|
lastOnerror.call(new Image() as HTMLImageElement, new Event("load"));
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(promise).rejects.toThrow();
|
await expect(promise).rejects.toThrow();
|
||||||
expect(imageConfigState.imageReady).toBe(false);
|
expect(imageState.imageReady).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { STATE_DATA, withPersistence } from "$states/persisted.svelte";
|
import { STATE_DATA, withPersistence } from "$states/persisted.svelte";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
const localStorageMock = (() => {
|
function createMock() {
|
||||||
let store: Record<string, string> = {};
|
let store: Record<string, string> = {};
|
||||||
return {
|
return {
|
||||||
getItem: vi.fn((key: string) => store[key] || null),
|
getItem: vi.fn((key: string) => store[key] || null),
|
||||||
@@ -15,13 +15,20 @@ const localStorageMock = (() => {
|
|||||||
store = {};
|
store = {};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
})();
|
}
|
||||||
|
|
||||||
Object.defineProperty(globalThis, "localStorage", {
|
|
||||||
value: localStorageMock,
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("persisted.svelte", () => {
|
describe("persisted.svelte", () => {
|
||||||
|
let localStorageMock: ReturnType<typeof createMock>;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
localStorageMock = createMock();
|
||||||
|
vi.stubGlobal("localStorage", localStorageMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorageMock.clear();
|
localStorageMock.clear();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ export default defineConfig({
|
|||||||
include: ["tests/**/*.{test,spec}.{js,ts}"],
|
include: ["tests/**/*.{test,spec}.{js,ts}"],
|
||||||
exclude: ["**/node_modules/**", "**/dist/**", "**/.svelte-kit/**", "**/build/**"],
|
exclude: ["**/node_modules/**", "**/dist/**", "**/.svelte-kit/**", "**/build/**"],
|
||||||
env: {
|
env: {
|
||||||
DEBUG_PRINT_LIMIT: "0",
|
DEBUG_PRINT_LIMIT: "2000",
|
||||||
},
|
},
|
||||||
coverage: {
|
coverage: {
|
||||||
provider: "istanbul",
|
provider: "istanbul",
|
||||||
|
|||||||
Reference in New Issue
Block a user