feat/e2e-testing (#31)
All checks were successful
🚀 Release / release (push) Successful in 4m7s
All checks were successful
🚀 Release / release (push) Successful in 4m7s
Reviewed-on: #31 Co-authored-by: Max Richter <max@max-richter.dev> Co-committed-by: Max Richter <max@max-richter.dev>
This commit was merged in pull request #31.
This commit is contained in:
@@ -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",
|
||||
|
||||
13
packages/ui/src/lib/Details.svelte.ts
Normal file
13
packages/ui/src/lib/Details.svelte.ts
Normal file
@@ -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();
|
||||
});
|
||||
});
|
||||
34
packages/ui/src/lib/ShortCut.svelte.ts
Normal file
34
packages/ui/src/lib/ShortCut.svelte.ts
Normal file
@@ -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();
|
||||
});
|
||||
});
|
||||
55
packages/ui/src/lib/helpers/getBoundingValue.test.ts
Normal file
55
packages/ui/src/lib/helpers/getBoundingValue.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } 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);
|
||||
});
|
||||
});
|
||||
27
packages/ui/src/lib/inputs/InputCheckbox.svelte.ts
Normal file
27
packages/ui/src/lib/inputs/InputCheckbox.svelte.ts
Normal file
@@ -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();
|
||||
});
|
||||
});
|
||||
45
packages/ui/src/lib/inputs/InputNumber.svelte.ts
Normal file
45
packages/ui/src/lib/inputs/InputNumber.svelte.ts
Normal file
@@ -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');
|
||||
});
|
||||
});
|
||||
27
packages/ui/src/lib/inputs/InputSelect.svelte.ts
Normal file
27
packages/ui/src/lib/inputs/InputSelect.svelte.ts
Normal file
@@ -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');
|
||||
});
|
||||
});
|
||||
26
packages/ui/src/lib/inputs/InputVec3.svelte.ts
Normal file
26
packages/ui/src/lib/inputs/InputVec3.svelte.ts
Normal file
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,7 @@ const config = {
|
||||
|
||||
kit: {
|
||||
paths: {
|
||||
base: BASE_URL
|
||||
base: BASE_URL === '/' ? '' : 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.
|
||||
|
||||
@@ -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}']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@ test('encode_float', () => {
|
||||
const input = 1.23;
|
||||
const encoded = encodeFloat(input);
|
||||
const output = decodeFloat(encoded);
|
||||
console.log(input, output);
|
||||
expect(output).toBeCloseTo(input);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,24 +21,6 @@ test('fastHashArray doesnt product collisions', () => {
|
||||
expect(hash_a).not.toEqual(hash_b);
|
||||
});
|
||||
|
||||
test('fastHashArray is fast(ish) < 20ms', () => {
|
||||
const a = new Int32Array(10_000);
|
||||
|
||||
const t0 = performance.now();
|
||||
fastHashArrayBuffer(a);
|
||||
|
||||
const t1 = performance.now();
|
||||
|
||||
a[0] = 1;
|
||||
|
||||
fastHashArrayBuffer(a);
|
||||
|
||||
const t2 = performance.now();
|
||||
|
||||
expect(t1 - t0).toBeLessThan(20);
|
||||
expect(t2 - t1).toBeLessThan(20);
|
||||
});
|
||||
|
||||
// test if the fastHashArray function is deterministic
|
||||
test('fastHashArray is deterministic', () => {
|
||||
const a = new Int32Array(1000);
|
||||
|
||||
@@ -8,8 +8,6 @@ test('it correctly concats nested arrays', () => {
|
||||
|
||||
const output = concatEncodedArrays([input_a, input_b, input_c]);
|
||||
|
||||
console.log('Output', output);
|
||||
|
||||
const decoded = decodeNestedArray(output);
|
||||
|
||||
expect(decoded[0]).toEqual([1, 2, 3]);
|
||||
|
||||
@@ -88,7 +88,7 @@ function decode_recursive(dense: number[] | Int32Array, index = 0) {
|
||||
dense,
|
||||
index
|
||||
);
|
||||
decoded.push(...p);
|
||||
decoded.push(p as number[]);
|
||||
index = nextIndex + 1;
|
||||
nextBracketIndex = _nextBracketIndex;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user