diff --git a/packages/ui/package.json b/packages/ui/package.json index 14241c9..fb105e0 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -34,14 +34,17 @@ "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.39.2", "@nodarium/types": "workspace:", + "@playwright/test": "^1.58.1", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.50.0", "@sveltejs/package": "^2.5.7", "@sveltejs/vite-plugin-svelte": "^6.2.4", + "@testing-library/svelte": "^5.3.1", "@types/eslint": "^9.6.1", "@types/three": "^0.182.0", "@typescript-eslint/eslint-plugin": "^8.53.0", "@typescript-eslint/parser": "^8.53.0", + "@vitest/browser-playwright": "^4.0.18", "dprint": "^0.51.1", "eslint": "^9.39.2", "eslint-plugin-svelte": "^3.14.0", @@ -54,7 +57,8 @@ "typescript": "^5.9.3", "typescript-eslint": "^8.54.0", "vite": "^7.3.1", - "vitest": "^4.0.17" + "vitest": "^4.0.18", + "vitest-browser-svelte": "^2.0.2" }, "svelte": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/ui/src/lib/Details.svelte.ts b/packages/ui/src/lib/Details.svelte.ts new file mode 100644 index 0000000..0edcfce --- /dev/null +++ b/packages/ui/src/lib/Details.svelte.ts @@ -0,0 +1,13 @@ +import { describe, expect, it } from 'vitest'; +import { render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import Details from './Details.svelte'; + +describe('Details', () => { + it('should render summary element', async () => { + render(Details, { title: 'Click me' }); + + const summary = page.getByText('Click me'); + await expect.element(summary).toBeInTheDocument(); + }); +}); diff --git a/packages/ui/src/lib/ShortCut.svelte.ts b/packages/ui/src/lib/ShortCut.svelte.ts new file mode 100644 index 0000000..af93a0e --- /dev/null +++ b/packages/ui/src/lib/ShortCut.svelte.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'vitest'; +import { render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import ShortCut from './ShortCut.svelte'; + +describe('ShortCut', () => { + it('should render with key label', async () => { + render(ShortCut, { key: 'S' }); + + const shortcut = page.getByText('S'); + await expect.element(shortcut).toBeInTheDocument(); + }); + + it('should render ctrl modifier', async () => { + render(ShortCut, { ctrl: true, key: 'S' }); + + const shortcut = page.getByText(/Ctrl/); + await expect.element(shortcut).toBeInTheDocument(); + }); + + it('should render alt modifier', async () => { + render(ShortCut, { alt: true, key: 'F4' }); + + const shortcut = page.getByText(/Alt/); + await expect.element(shortcut).toBeInTheDocument(); + }); + + it('should render multiple modifiers', async () => { + render(ShortCut, { ctrl: true, alt: true, key: 'Delete' }); + + const shortcut = page.getByText(/Ctrl/); + await expect.element(shortcut).toBeInTheDocument(); + }); +}); diff --git a/packages/ui/src/lib/helpers/getBoundingValue.test.ts b/packages/ui/src/lib/helpers/getBoundingValue.test.ts new file mode 100644 index 0000000..a54b3e7 --- /dev/null +++ b/packages/ui/src/lib/helpers/getBoundingValue.test.ts @@ -0,0 +1,55 @@ +import { describe, it, expect } from 'vitest'; +import { getBoundingValue } from './getBoundingValue'; + +describe('getBoundingValue', () => { + it('should return 1 for values between 0 and 1', () => { + expect(getBoundingValue(0)).toBe(1); + expect(getBoundingValue(0.5)).toBe(1); + expect(getBoundingValue(1)).toBe(1); + }); + + it('should return 2 for values between 1 and 2', () => { + expect(getBoundingValue(1.1)).toBe(2); + expect(getBoundingValue(1.5)).toBe(2); + expect(getBoundingValue(2)).toBe(2); + }); + + it('should return 4 for values between 2 and 4', () => { + expect(getBoundingValue(2.1)).toBe(4); + expect(getBoundingValue(3)).toBe(4); + expect(getBoundingValue(4)).toBe(4); + }); + + it('should return positive level for positive input', () => { + expect(getBoundingValue(5)).toBe(10); + expect(getBoundingValue(15)).toBe(20); + expect(getBoundingValue(50)).toBe(50); + expect(getBoundingValue(150)).toBe(200); + }); + + it('should return negative level for negative input', () => { + expect(getBoundingValue(-5)).toBe(-10); + expect(getBoundingValue(-15)).toBe(-20); + expect(getBoundingValue(-50)).toBe(-50); + expect(getBoundingValue(-150)).toBe(-200); + }); + + it('should return correct level for boundary values', () => { + expect(getBoundingValue(10)).toBe(10); + expect(getBoundingValue(20)).toBe(20); + expect(getBoundingValue(50)).toBe(50); + expect(getBoundingValue(100)).toBe(100); + expect(getBoundingValue(200)).toBe(200); + }); + + it('should handle large values', () => { + expect(getBoundingValue(1000)).toBe(1000); + expect(getBoundingValue(1500)).toBe(1000); + expect(getBoundingValue(-1000)).toBe(-1000); + }); + + it('should handle very small values', () => { + expect(getBoundingValue(0.001)).toBe(1); + expect(getBoundingValue(-0.001)).toBe(-1); + }); +}); diff --git a/packages/ui/src/lib/inputs/InputCheckbox.svelte.ts b/packages/ui/src/lib/inputs/InputCheckbox.svelte.ts new file mode 100644 index 0000000..b2589fc --- /dev/null +++ b/packages/ui/src/lib/inputs/InputCheckbox.svelte.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'vitest'; +import { render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import InputCheckbox from './InputCheckbox.svelte'; + +describe('InputCheckbox', () => { + it('should render checkbox label', async () => { + render(InputCheckbox, { value: false }); + + const checkbox = page.getByRole('checkbox'); + await expect.element(checkbox).toBeInTheDocument(); + }); + + it('should be unchecked when value is false', async () => { + render(InputCheckbox, { value: false }); + + const input = page.getByRole('checkbox'); + await expect.element(input).not.toBeChecked(); + }); + + it('should be checked when value is true', async () => { + render(InputCheckbox, { value: true }); + + const input = page.getByRole('checkbox'); + await expect.element(input).toBeChecked(); + }); +}); diff --git a/packages/ui/src/lib/inputs/InputNumber.svelte.ts b/packages/ui/src/lib/inputs/InputNumber.svelte.ts new file mode 100644 index 0000000..1ed2d17 --- /dev/null +++ b/packages/ui/src/lib/inputs/InputNumber.svelte.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest'; +import { render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import InputNumber from './InputNumber.svelte'; + +describe('InputNumber', () => { + it('should render input element', async () => { + render(InputNumber, { value: 0.5 }); + + const input = page.getByRole('spinbutton'); + await expect.element(input).toBeInTheDocument(); + }); + + it('should render with step buttons when step is provided', async () => { + render(InputNumber, { value: 0.5, step: 0.1 }); + + const decrementBtn = page.getByRole('button', { name: 'step down' }); + const incrementBtn = page.getByRole('button', { name: 'step up' }); + await expect.element(decrementBtn).toBeInTheDocument(); + await expect.element(incrementBtn).toBeInTheDocument(); + }); + + it('should not render step buttons when step is undefined', async () => { + const screen = render(InputNumber, { value: 0.5 }); + + const buttons = screen.locator.getByRole('button'); + const count = buttons.all().length; + expect(count).toBe(0); + }); + + it('should accept numeric value', async () => { + render(InputNumber, { value: 42 }); + + const input = page.getByRole('spinbutton'); + await expect.element(input).toHaveValue(42); + }); + + it('should accept min and max bounds', async () => { + render(InputNumber, { value: 5, min: 0, max: 10 }); + + const input = page.getByRole('spinbutton'); + await expect.element(input).toHaveAttribute('min', '0'); + await expect.element(input).toHaveAttribute('max', '10'); + }); +}); diff --git a/packages/ui/src/lib/inputs/InputSelect.svelte.ts b/packages/ui/src/lib/inputs/InputSelect.svelte.ts new file mode 100644 index 0000000..4188147 --- /dev/null +++ b/packages/ui/src/lib/inputs/InputSelect.svelte.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'vitest'; +import { render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import InputSelect from './InputSelect.svelte'; + +describe('InputSelect', () => { + it('should render select element', async () => { + render(InputSelect, { options: ['a', 'b', 'c'], value: 0 }); + + const select = page.getByRole('combobox'); + await expect.element(select).toBeInTheDocument(); + }); + + it('should render all options', async () => { + render(InputSelect, { options: ['apple', 'banana', 'cherry'], value: 0 }); + + const select = page.getByRole('combobox'); + await expect.element(select).toHaveTextContent('applebananacherry'); + }); + + it('should select correct option by index', async () => { + render(InputSelect, { options: ['first', 'second', 'third'], value: 1 }); + + const select = page.getByRole('combobox'); + await expect.element(select).toHaveTextContent('second'); + }); +}); diff --git a/packages/ui/src/lib/inputs/InputVec3.svelte.ts b/packages/ui/src/lib/inputs/InputVec3.svelte.ts new file mode 100644 index 0000000..baaaefb --- /dev/null +++ b/packages/ui/src/lib/inputs/InputVec3.svelte.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest'; +import { render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import InputVec3 from './InputVec3.svelte'; + +describe('InputVec3', () => { + it('should render with correct initial values', async () => { + const value = $state([1.5, 2.5, 3.5]); + render(InputVec3, { value }); + + const inputs = page.getByRole('spinbutton'); + await expect.element(inputs.first()).toBeInTheDocument(); + + expect(inputs.nth(0)).toHaveValue(1.5); + expect(inputs.nth(1)).toHaveValue(2.5); + expect(inputs.nth(2)).toHaveValue(3.5); + }); + + it('should have step attribute', async () => { + const value = $state([0, 0, 0]); + render(InputVec3, { value }); + + const input = page.getByRole('spinbutton').first(); + await expect.element(input).toHaveAttribute('step', '0.01'); + }); +}); diff --git a/packages/ui/svelte.config.js b/packages/ui/svelte.config.js index a13a085..35b2cd7 100644 --- a/packages/ui/svelte.config.js +++ b/packages/ui/svelte.config.js @@ -1,8 +1,6 @@ import adapter from '@sveltejs/adapter-static'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; -const { BASE_URL = '' } = process.env; - /** @type {import('@sveltejs/kit').Config} */ const config = { // Consult https://kit.svelte.dev/docs/integrations#preprocessors @@ -10,9 +8,6 @@ const config = { preprocess: vitePreprocess(), kit: { - paths: { - base: BASE_URL - }, // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. // If your environment is not supported, or you settled on a specific environment, switch out the adapter. // See https://kit.svelte.dev/docs/adapters for more information about adapters. diff --git a/packages/ui/vite.config.ts b/packages/ui/vite.config.ts index 71c336a..181246f 100644 --- a/packages/ui/vite.config.ts +++ b/packages/ui/vite.config.ts @@ -1,5 +1,6 @@ import { sveltekit } from '@sveltejs/kit/vite'; import tailwindcss from '@tailwindcss/vite'; +import { playwright } from '@vitest/browser-playwright'; import { exec } from 'node:child_process'; import { readFileSync } from 'node:fs'; import { defineConfig } from 'vitest/config'; @@ -21,6 +22,31 @@ const postDevPackagePlugin = () => { export default defineConfig({ plugins: [tailwindcss(), sveltekit(), postDevPackagePlugin()], test: { - include: ['src/**/*.{test,spec}.{js,ts}'] + expect: { requireAssertions: true }, + projects: [ + { + extends: './vite.config.ts', + test: { + name: 'client', + browser: { + enabled: true, + provider: playwright(), + instances: [{ browser: 'firefox', headless: true }] + }, + include: ['src/**/*.svelte.ts'], + exclude: ['src/lib/server/**'] + } + }, + + { + extends: './vite.config.ts', + test: { + name: 'server', + environment: 'node', + include: ['src/**/*.{test,spec}.{js,ts}'], + exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'] + } + } + ] } }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff41698..945bbe2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,12 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + chokidar-cli: + specifier: github:open-cli-tools/chokidar-cli#semver:v4.0.0 + version: 4.0.0 + importers: .: @@ -179,6 +185,9 @@ importers: '@nodarium/types': specifier: 'workspace:' version: link:../types + '@playwright/test': + specifier: ^1.58.1 + version: 1.58.1 '@sveltejs/adapter-static': specifier: ^3.0.10 version: 3.0.10(@sveltejs/kit@2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))) @@ -191,6 +200,9 @@ importers: '@sveltejs/vite-plugin-svelte': specifier: ^6.2.4 version: 6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2)) + '@testing-library/svelte': + specifier: ^5.3.1 + version: 5.3.1(svelte@5.46.4)(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))(vitest@4.0.18) '@types/eslint': specifier: ^9.6.1 version: 9.6.1 @@ -203,6 +215,9 @@ importers: '@typescript-eslint/parser': specifier: ^8.53.0 version: 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@vitest/browser-playwright': + specifier: ^4.0.18 + version: 4.0.18(playwright@1.58.1)(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))(vitest@4.0.18) dprint: specifier: ^0.51.1 version: 0.51.1 @@ -240,8 +255,11 @@ importers: specifier: ^7.3.1 version: 7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) vitest: - specifier: ^4.0.17 - version: 4.0.17(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + specifier: ^4.0.18 + version: 4.0.18(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + vitest-browser-svelte: + specifier: ^2.0.2 + version: 2.0.2(svelte@5.46.4)(vitest@4.0.18) packages/utils: dependencies: @@ -257,7 +275,7 @@ importers: version: 7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) vitest: specifier: ^4.0.17 - version: 4.0.17(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + version: 4.0.17(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) packages: @@ -267,6 +285,18 @@ packages: '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -1138,12 +1168,29 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + '@testing-library/svelte-core@1.0.0': resolution: {integrity: sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ==} engines: {node: '>=16'} peerDependencies: svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 + '@testing-library/svelte@5.3.1': + resolution: {integrity: sha512-8Ez7ZOqW5geRf9PF5rkuopODe5RGy3I9XR+kc7zHh26gBiktLaxTfKmhlGaSHYUOTQE7wFsLMN9xCJVCszw47w==} + engines: {node: '>= 10'} + peerDependencies: + svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 + vite: '*' + vitest: '*' + peerDependenciesMeta: + vite: + optional: true + vitest: + optional: true + '@threejs-kit/instanced-sprite-mesh@2.5.1': resolution: {integrity: sha512-pmt1ALRhbHhCJQTj2FuthH6PeLIeaM4hOuS2JO3kWSwlnvx/9xuUkjFR3JOi/myMqsH7pSsLIROSaBxDfttjeA==} peerDependencies: @@ -1179,6 +1226,9 @@ packages: '@tweenjs/tween.js@23.1.3': resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1344,6 +1394,9 @@ packages: '@vitest/expect@4.0.17': resolution: {integrity: sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ==} + '@vitest/expect@4.0.18': + resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + '@vitest/mocker@4.0.17': resolution: {integrity: sha512-+ZtQhLA3lDh1tI2wxe3yMsGzbp7uuJSWBM1iTIKCbppWTSBN09PUC+L+fyNlQApQoR+Ps8twt2pbSSXg2fQVEQ==} peerDependencies: @@ -1375,9 +1428,15 @@ packages: '@vitest/runner@4.0.17': resolution: {integrity: sha512-JmuQyf8aMWoo/LmNFppdpkfRVHJcsgzkbCA+/Bk7VfNH7RE6Ut2qxegeyx2j3ojtJtKIbIGy3h+KxGfYfk28YQ==} + '@vitest/runner@4.0.18': + resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} + '@vitest/snapshot@4.0.17': resolution: {integrity: sha512-npPelD7oyL+YQM2gbIYvlavlMVWUfNNGZPcu0aEUQXt7FXTuqhmgiYupPnAanhKvyP6Srs2pIbWo30K0RbDtRQ==} + '@vitest/snapshot@4.0.18': + resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} + '@vitest/spy@4.0.17': resolution: {integrity: sha512-I1bQo8QaP6tZlTomQNWKJE6ym4SHf3oLS7ceNjozxxgzavRAgZDc06T7kD8gb9bXKEgcLNt00Z+kZO6KaJ62Ew==} @@ -1414,6 +1473,10 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} @@ -1422,6 +1485,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} @@ -1436,6 +1503,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -1624,6 +1694,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1638,6 +1712,9 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -1983,6 +2060,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true @@ -2127,6 +2207,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + maath@0.10.8: resolution: {integrity: sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==} peerDependencies: @@ -2338,6 +2422,10 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} @@ -2350,6 +2438,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -2766,6 +2857,40 @@ packages: jsdom: optional: true + vitest@4.0.18: + resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.18 + '@vitest/browser-preview': 4.0.18 + '@vitest/browser-webdriverio': 4.0.18 + '@vitest/ui': 4.0.18 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -2873,6 +2998,16 @@ snapshots: lru-cache: 10.4.3 optional: true + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/runtime@7.28.6': {} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -3501,10 +3636,30 @@ snapshots: tailwindcss: 4.1.18 vite: 7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.28.6 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + '@testing-library/svelte-core@1.0.0(svelte@5.46.4)': dependencies: svelte: 5.46.4 + '@testing-library/svelte@5.3.1(svelte@5.46.4)(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))(vitest@4.0.18)': + dependencies: + '@testing-library/dom': 10.4.1 + '@testing-library/svelte-core': 1.0.0(svelte@5.46.4) + svelte: 5.46.4 + optionalDependencies: + vite: 7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + vitest: 4.0.18(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + '@threejs-kit/instanced-sprite-mesh@2.5.1(@types/three@0.182.0)(three@0.182.0)': dependencies: diet-sprite: 0.0.1 @@ -3551,6 +3706,8 @@ snapshots: '@tweenjs/tween.js@23.1.3': {} + '@types/aria-query@5.0.4': {} + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -3785,6 +3942,19 @@ snapshots: - utf-8-validate - vite + '@vitest/browser-playwright@4.0.18(playwright@1.58.1)(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))(vitest@4.0.18)': + dependencies: + '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))(vitest@4.0.18) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2)) + playwright: 1.58.1 + tinyrainbow: 3.0.3 + vitest: 4.0.18(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + '@vitest/browser@4.0.18(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))(vitest@4.0.17)': dependencies: '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2)) @@ -3802,6 +3972,23 @@ snapshots: - utf-8-validate - vite + '@vitest/browser@4.0.18(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))(vitest@4.0.18)': + dependencies: + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2)) + '@vitest/utils': 4.0.18 + magic-string: 0.30.21 + pixelmatch: 7.1.0 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.0.3 + vitest: 4.0.18(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + '@vitest/expect@4.0.17': dependencies: '@standard-schema/spec': 1.1.0 @@ -3811,15 +3998,16 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.17(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))': + '@vitest/expect@4.0.18': dependencies: - '@vitest/spy': 4.0.17 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + chai: 6.2.2 + tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.17(vite@7.3.1(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))': + '@vitest/mocker@4.0.17(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))': dependencies: '@vitest/spy': 4.0.17 estree-walker: 3.0.3 @@ -3848,12 +4036,23 @@ snapshots: '@vitest/utils': 4.0.17 pathe: 2.0.3 + '@vitest/runner@4.0.18': + dependencies: + '@vitest/utils': 4.0.18 + pathe: 2.0.3 + '@vitest/snapshot@4.0.17': dependencies: '@vitest/pretty-format': 4.0.17 magic-string: 0.30.21 pathe: 2.0.3 + '@vitest/snapshot@4.0.18': + dependencies: + '@vitest/pretty-format': 4.0.18 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@4.0.17': {} '@vitest/spy@4.0.18': {} @@ -3891,12 +4090,16 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} anymatch@3.1.3: @@ -3909,6 +4112,10 @@ snapshots: argparse@2.0.1: {} + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + aria-query@5.3.2: {} assertion-error@2.0.1: {} @@ -4090,6 +4297,8 @@ snapshots: delayed-stream@1.0.0: optional: true + dequal@2.0.3: {} + detect-libc@2.1.2: {} devalue@5.6.2: {} @@ -4099,6 +4308,8 @@ snapshots: diff@4.0.2: optional: true + dom-accessibility-api@0.5.16: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -4526,6 +4737,8 @@ snapshots: jiti@2.6.1: {} + js-tokens@4.0.0: {} + js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -4665,6 +4878,8 @@ snapshots: lru-cache@10.4.3: optional: true + lz-string@1.5.0: {} + maath@0.10.8(@types/three@0.182.0)(three@0.182.0): dependencies: '@types/three': 0.182.0 @@ -4851,6 +5066,12 @@ snapshots: prelude-ls@1.2.1: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + prr@1.0.1: optional: true @@ -4863,6 +5084,8 @@ snapshots: punycode@2.3.1: {} + react-is@17.0.2: {} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -5250,6 +5473,12 @@ snapshots: svelte: 5.46.4 vitest: 4.0.17(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + vitest-browser-svelte@2.0.2(svelte@5.46.4)(vitest@4.0.18): + dependencies: + '@testing-library/svelte-core': 1.0.0(svelte@5.46.4) + svelte: 5.46.4 + vitest: 4.0.18(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) + vitest@4.0.17(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2): dependencies: '@vitest/expect': 4.0.17 @@ -5289,15 +5518,15 @@ snapshots: - tsx - yaml - vitest@4.0.17(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2): + vitest@4.0.18(@types/node@22.8.6)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2): dependencies: - '@vitest/expect': 4.0.17 - '@vitest/mocker': 4.0.17(vite@7.3.1(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2)) - '@vitest/pretty-format': 4.0.17 - '@vitest/runner': 4.0.17 - '@vitest/snapshot': 4.0.17 - '@vitest/spy': 4.0.17 - '@vitest/utils': 4.0.17 + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 es-module-lexer: 1.7.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -5312,6 +5541,8 @@ snapshots: vite: 7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2) why-is-node-running: 2.3.0 optionalDependencies: + '@types/node': 22.8.6 + '@vitest/browser-playwright': 4.0.18(playwright@1.58.1)(vite@7.3.1(@types/node@22.8.6)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.80.6)(terser@5.36.0)(tsx@4.19.2))(vitest@4.0.18) jsdom: 25.0.1 transitivePeerDependencies: - jiti