Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a0a5dab
test: add integration-tests
sdobbert Feb 19, 2026
c42f125
fix: fix linting errors
sdobbert Feb 19, 2026
09b8140
test: add negative tests for readonly
sdobbert Feb 19, 2026
8e94483
fix: update vite.config.ts
sdobbert Feb 19, 2026
07094d2
fix: lifecycle_outside_component error in tests
sdobbert Feb 19, 2026
1e7b160
fix: exclude i18n from coverage
sdobbert Feb 19, 2026
e701f98
fix: assign correct metadata type
sdobbert Feb 19, 2026
93eba0f
fix: minor type fix
sdobbert Feb 19, 2026
9da3594
fix: suppress custom_element_props warning from eslint
sdobbert Feb 19, 2026
680e07b
fix: linting errors
sdobbert Feb 19, 2026
de62287
Merge branch 'main' into add-integration-tests
sdobbert Feb 26, 2026
5c67b9b
fix: update tests
sdobbert Feb 26, 2026
a86111c
fix: date format in tests
sdobbert Feb 26, 2026
8403018
fix: add helper function for service subform tests
sdobbert Feb 26, 2026
eca8236
fix: dispatch-error
sdobbert Feb 26, 2026
d99c87b
fix: make service form tests more robust
sdobbert Feb 26, 2026
deaef33
Merge branch 'main' of github.com:gdi-be/mde-client into add-integrat…
sdobbert Mar 17, 2026
7ab1b5d
fix: resolve conflicts
sdobbert Mar 17, 2026
9dcabe4
fix: timing issues in tests
sdobbert Mar 17, 2026
56e4d64
fix: userEvent timing issues
sdobbert Mar 17, 2026
f8b1e68
fix: smui timing issue in tests
sdobbert Mar 17, 2026
d952a80
fix: add async drain after each test
sdobbert Mar 18, 2026
9a8d9b4
fix: closed channel issue in tests
sdobbert Mar 18, 2026
f9a29ad
fix: remove unneeded cleanup
sdobbert Mar 18, 2026
5bfe626
fix: select timing in tests
sdobbert Mar 18, 2026
9f3dcbf
fix: select timing in tests
sdobbert Mar 18, 2026
5aec645
fix: test for terms of use field
sdobbert Mar 18, 2026
8612966
fix: test for terms of use field
sdobbert Mar 18, 2026
bce6ae0
fix: orevent smui select crash
sdobbert Mar 18, 2026
1ceb789
fix: switch to jsdom
sdobbert Mar 18, 2026
24ade11
fix: ignore smui errors after test run
sdobbert Mar 18, 2026
646494e
fix: prettier issues
sdobbert Mar 18, 2026
03be278
fix: remove unneeded eslint exceptions
sdobbert Mar 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ vite.config.ts.timestamp-*
# Svelte
smui.css
smui-dark.css

# Test
/coverage
/html
486 changes: 394 additions & 92 deletions bun.lock

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,38 @@
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "prettier --check . && eslint .",
"test": "bun test",
"test": "vitest",
"test:coverage": "vitest --coverage",
"test:report": "vitest --ui --reporter=html",
"prepare": "npm run smui-theme-light",
"smui-theme-light": "smui-theme compile static/smui.css -i src/theme"
},
"devDependencies": {
"@smui/fab": "^8.0.3",
"@sveltejs/kit": "^2.53.1",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/svelte": "^5.3.1",
"@testing-library/user-event": "^14.6.1",
"@types/bun": "^1.3.9",
"@types/eslint": "^9.6.1",
"@vitest/coverage-v8": "3.2.4",
"@vitest/ui": "3.2.4",
"bun-types": "^1.3.9",
"eslint": "^10.0.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.15.0",
"globals": "^17.3.0",
"jsdom": "^29.0.0",
"prettier": "^3.8.1",
"prettier-plugin-svelte": "^3.5.0",
"svelte": "^5.53.3",
"svelte-adapter-bun": "^1.0.1",
"svelte-check": "^4.4.3",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.1",
"vite": "^7.3.1"
"vite": "^7.3.1",
"vitest": "^3.2.4"
},
"dependencies": {
"@eslint/js": "^10.0.1",
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/Form/Field/10_PublishedField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
};
</script>

<div class="date-time-field">
<div class="published-field">
<DateInput
bind:value
key={KEY}
Expand All @@ -53,7 +53,7 @@
</div>

<style lang="scss">
.date-time-field {
.published-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/Form/Field/11_LastUpdatedField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
};
</script>

<div class="date-time-field">
<div class="last-updated-field">
<DateInput
bind:value
key={KEY}
Expand All @@ -53,7 +53,7 @@
</div>

<style lang="scss">
.date-time-field {
.last-updated-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
};
</script>

<div class="metadata-type-field">
<div class="maintenance-frequency-field">
<SelectInput
label={t('14_MaintenanceFrequencyField.label')}
explanation={t('14_MaintenanceFrequencyField.explanation')}
Expand All @@ -57,7 +57,7 @@
</div>

<style lang="scss">
.metadata-type-field {
.maintenance-frequency-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
};
</script>

<div class="title-field">
<div class="delivered-crs-field">
<TextInput
bind:value
label={t('16_DeliveredCoordinateSystemField.label')}
Expand All @@ -38,7 +38,7 @@
</div>

<style lang="scss">
.title-field {
.delivered-crs-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/Form/Field/17_CoordinateSystemField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
</script>

{#if highestRole !== 'MdeDataOwner'}
<div class="title-field">
<div class="crs-field">
{#await fetchOptions()}
<p>{t('general.loading_options')}</p>
{:then OPTIONS}
Expand All @@ -64,7 +64,7 @@
{/if}

<style lang="scss">
.title-field {
.crs-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/Form/Field/28_ResolutionField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
};
</script>

<div class="title-field">
<div class="resolution-field">
<fieldset class={[hasInvalidFields ? 'invalid' : '']}>
<legend>{t('28_ResolutionField.label')}</legend>
<FieldHint explanation={t('28_ResolutionField.explanation')} />
Expand Down Expand Up @@ -163,7 +163,7 @@
</div>

<style lang="scss">
.title-field {
.resolution-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/Form/Field/30_ContentDescription.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
};
</script>

<div class="technical-description-field">
<div class="content-description-field">
<TextInput
bind:value
label={t('30_ContentDescription.label')}
Expand All @@ -41,7 +41,7 @@
</div>

<style lang="scss">
.technical-description-field {
.content-description-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/Form/Inputs/NumberInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
onblur,
fieldConfig,
onfocus,
// eslint-disable-next-line
...restProps
}: InputProps = $props();

Expand Down
1 change: 1 addition & 0 deletions src/lib/components/Form/Inputs/SelectInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
options,
disabled = false,
validationResult,
// eslint-disable-next-line
...restProps
}: InputProps = $props();

Expand Down
1 change: 1 addition & 0 deletions src/lib/components/Form/Inputs/TextAreaInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
validationResult,
onblur,
onfocus,
// eslint-disable-next-line
...restProps
}: InputProps = $props();

Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/Form/Inputs/TextInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
onblur,
onfocus,
explanation,
// eslint-disable-next-line
...restProps
}: InputProps = $props();

Expand Down Expand Up @@ -69,7 +70,7 @@
<FieldHint {validationResult} {fieldConfig} {fieldHasFocus} {explanation} />
{#if maxlength}
<div class="character-counter">
{value.length} / {maxlength}
{value?.length ?? 0} / {maxlength}
</div>
{/if}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
const validationResult = $derived(fieldConfig?.validator(value));
</script>

<div class="layer-short-description-field">
<div class="layer-datasource-field">
<TextInput
label={t('55_LayerDatasource.label')}
explanation={t('55_LayerDatasource.explanation')}
Expand All @@ -38,7 +38,7 @@
</div>

<style lang="scss">
.layer-short-description-field {
.layer-datasource-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</script>

{#if fieldVisible}
<div class="layer-short-description-field">
<div class="layer-secondary-datasource-field">
<TextInput
label={t('68_LayerSecondaryDatasource.label')}
{value}
Expand All @@ -45,7 +45,7 @@
{/if}

<style lang="scss">
.layer-short-description-field {
.layer-secondary-datasource-field {
position: relative;
display: flex;
gap: 0.25em;
Expand Down
64 changes: 40 additions & 24 deletions tests/FormContext.spec.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,76 @@
import { describe, expect, test } from 'bun:test';
import { getFormContext } from '$lib/context/FormContext.svelte';
import { describe, expect, test } from 'vitest';
import { MetadataService } from '$lib/services/MetadataService';
import metadata1 from './fixtures/metadata1';
import type { Contact } from '$lib/models/metadata';

const { getValue, getAllValues } = getFormContext();
//const { getValue, getAllValues } = getFormContext();

describe('FormContext', () => {
describe('getValue', () => {
test('should get simple values', () => {
expect(getValue<string>('title', metadata1)).toBe('123 Datentest');
expect(getValue<string>('isoMetadata.title', metadata1)).toBe('123 Datentest');
expect(getValue<string>('isoMetadata.description', metadata1)).toBe(
expect(MetadataService.getValue<string>('title', metadata1)).toBe('123 Datentest');
expect(MetadataService.getValue<string>('isoMetadata.title', metadata1)).toBe(
'123 Datentest'
);
expect(MetadataService.getValue<string>('isoMetadata.description', metadata1)).toBe(
'wergwergergergergergrge'
);
expect(getValue<Contact[]>('isoMetadata.pointsOfContact', metadata1)).toEqual([
{
id: 'b3e1c2d4-5f6a-4e7b-8c9d-0a1b2c3d4e5f',
name: 'Peter Klose',
organisation: 'terrestris GmbH & Co Kg',
phone: '123123123',
email: 'klose@wefewf.de'
}
]);
expect(MetadataService.getValue<Contact[]>('isoMetadata.pointsOfContact', metadata1)).toEqual(
[
{
id: 'b3e1c2d4-5f6a-4e7b-8c9d-0a1b2c3d4e5f',
name: 'Peter Klose',
organisation: 'terrestris GmbH & Co Kg',
phone: '123123123',
email: 'klose@wefewf.de'
}
]
);
});

test('should get nested array values with index', () => {
expect(getValue<string>('isoMetadata.services[0].title', metadata1)).toBe('123 Datentest');
expect(getValue<string>('isoMetadata.services[1].serviceType', metadata1)).toBe('ATOM');
expect(MetadataService.getValue<string>('isoMetadata.services[0].title', metadata1)).toBe(
'123 Datentest'
);
expect(
MetadataService.getValue<string>('isoMetadata.services[1].serviceType', metadata1)
).toBe('ATOM');
expect(
getValue<string>('isoMetadata.services[2].featureTypes[0].columns[0].name', metadata1)
MetadataService.getValue<string>(
'isoMetadata.services[2].featureTypes[0].columns[0].name',
metadata1
)
).toBe('Mein Attribut');
});

test('should return undefined for non-existent paths', () => {
expect(getValue('isoMetadata.nonexistent.path', metadata1)).toBeUndefined();
expect(getValue('isoMetadata.services[99]', metadata1)).toBeUndefined();
expect(MetadataService.getValue('isoMetadata.nonexistent.path', metadata1)).toBeUndefined();
expect(MetadataService.getValue('isoMetadata.services[99]', metadata1)).toBeUndefined();
});
});

describe('getAllValues', () => {
test('should collect all column names from all feature types', () => {
const names = getAllValues('isoMetadata.services.featureTypes.columns.name', metadata1);
const names = MetadataService.getAllValues(
'isoMetadata.services.featureTypes.columns.name',
metadata1
);
expect(names).toEqual(['Mein Attribut', 'Mein zweites Attribut ']);
});

test('should collect all service titles', () => {
const titles = getAllValues('isoMetadata.services.title', metadata1);
const titles = MetadataService.getAllValues('isoMetadata.services.title', metadata1);
expect(titles).toEqual(['123 Datentest', 'ewfewf', 'WFS Titel']);
});

test('should handle special case for layers (profileId 48)', () => {
const layers = getAllValues('clientMetadata.layers', metadata1);
const layers = MetadataService.getAllValues('clientMetadata.layers', metadata1);
expect(layers).toHaveLength(1); // Nach dem Flatten
expect(layers[0]).toHaveProperty('name', 'ewfwefew');
});

test('should collect keywords as expected', () => {
const keywords = getAllValues('isoMetadata.keywords', metadata1);
const keywords = MetadataService.getAllValues('isoMetadata.keywords', metadata1);
expect(keywords).toEqual([
{
default: [
Expand Down
4 changes: 2 additions & 2 deletions tests/MetadataService.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, test } from 'bun:test';
import { describe, expect, test } from 'vitest';
import { MetadataService } from '$lib/services/MetadataService';
import metadata1 from './fixtures/metadata1';
import metadata2 from './fixtures/metadata2';
Expand Down Expand Up @@ -153,7 +153,7 @@ describe('MetadataService', () => {

test('should get nested service properties', () => {
const result = MetadataService.getValue('isoMetadata.services[0].workspace', metadata1);
expect(result).toBe('ewfewf');
expect(result).toBe('workspace1');
});

test('should get clientMetadata', () => {
Expand Down
Loading
Loading