generated from Seekra/repository-template
Feat(settings): add renderer for settings configuration #142
@@ -84,15 +84,15 @@ onMounted(async () => {
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<div v-if="!settingsLoaded">
|
||||
<div v-if="!settingsLoaded" class="main-content--loading">
|
||||
{{ t('loading') }}
|
||||
</div>
|
||||
<div v-else-if="activeSection">
|
||||
<div v-else-if="activeSection" class="main-content--loaded">
|
||||
<div class="settings-main-content">
|
||||
<SettingsPage :setting="activeSectionContent" :path="activeSection" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-else class="main-content--no-section">
|
||||
{{ t('settings.noSectionSelected') }}
|
||||
</div>
|
||||
</LeftSidebarLayout>
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
Copyright 2026 Seekra
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import SettingsView from '../SettingsView.vue';
|
||||
import { mountComponent } from '@/test-utils/mountComponent.js';
|
||||
import { flushPromises } from '@vue/test-utils';
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
const settingsHoisted = vi.hoisted(() => [
|
||||
{
|
||||
name: 's0',
|
||||
i18n: 'settings.s0',
|
||||
content: []
|
||||
},
|
||||
{
|
||||
name: 's1',
|
||||
i18n: 'settings.s1',
|
||||
content: [
|
||||
{
|
||||
type: 'bool',
|
||||
name: 'b0',
|
||||
i18n: 'settings.s1.b0'
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'n0',
|
||||
i18n: 'settings.s1.n0'
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'st0',
|
||||
i18n: 'settings.s1.st0'
|
||||
},
|
||||
{
|
||||
type: 'selection',
|
||||
name: 'se0',
|
||||
i18n: 'settings.s1.se0',
|
||||
options: [
|
||||
{ name: 'o0', i18n: 'settings.s1.se0.o0' },
|
||||
{ name: 'o1', i18n: 'settings.s1.se0.o1' },
|
||||
{ name: 'o2', i18n: 'settings.s1.se0.o2' },
|
||||
{ name: 'o3', i18n: 'settings.s1.se0.o3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'section',
|
||||
name: 's0',
|
||||
i18n: 'settings.s1.s0',
|
||||
content: []
|
||||
},
|
||||
{
|
||||
type: 'section',
|
||||
name: 's1',
|
||||
i18n: 'settings.s1.s1',
|
||||
content: [
|
||||
{
|
||||
type: 'bool',
|
||||
name: 'b0',
|
||||
i18n: 'settings.s1.s1.b0',
|
||||
default: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 's2',
|
||||
i18n: 'settings.s2',
|
||||
content: [
|
||||
{
|
||||
type: 'section',
|
||||
name: 's0',
|
||||
i18n: 'settings.s2.s0',
|
||||
content: [
|
||||
{
|
||||
type: 'bool',
|
||||
name: 'b0',
|
||||
i18n: 'settings.s2.s0.b0',
|
||||
default: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 's3',
|
||||
i18n: 'settings.s3',
|
||||
content: [
|
||||
{
|
||||
type: 'section',
|
||||
name: 's0',
|
||||
i18n: 'settings.s3.s0',
|
||||
content: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 's4',
|
||||
i18n: 'settings.s4',
|
||||
content: [
|
||||
{
|
||||
type: 'selection',
|
||||
name: 'se0',
|
||||
i18n: 'settings.s4.se0',
|
||||
options: [
|
||||
{ name: 'o0', i18n: 'settings.s4.se0.o0' },
|
||||
{ name: 'o1', i18n: 'settings.s4.se0.o1' },
|
||||
{ name: 'o2', i18n: 'settings.s4.se0.o2' },
|
||||
{ name: 'o3', i18n: 'settings.s4.se0.o3' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 's5',
|
||||
i18n: 'settings.s5',
|
||||
content: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'st0',
|
||||
i18n: 'settings.s5.st0'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 's6',
|
||||
i18n: 'settings.s6',
|
||||
content: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'n0',
|
||||
i18n: 'settings.s6.n0'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 's7',
|
||||
i18n: 'settings.s7',
|
||||
content: [
|
||||
{
|
||||
type: 'bool',
|
||||
name: 'b0',
|
||||
i18n: 'settings.s7.b0'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 's8',
|
||||
i18n: 'settings.s8',
|
||||
content: [
|
||||
{
|
||||
type: 'section',
|
||||
name: 's0',
|
||||
i18n: 'settings.s8.s0',
|
||||
content: [
|
||||
{
|
||||
type: 'section',
|
||||
name: 's0',
|
||||
i18n: 'settings.s8.s0.s0',
|
||||
content: [
|
||||
{
|
||||
type: 'section',
|
||||
name: 's0',
|
||||
i18n: 'settings.s8.s0.s0.s0',
|
||||
content: [
|
||||
{
|
||||
type: 'selection',
|
||||
name: 'se0',
|
||||
i18n: 'settings.s8.s0.s0.s0.se0',
|
||||
allowMultiple: true,
|
||||
options: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
const settings = settingsHoisted.values();
|
||||
|
||||
const originalCurrentSection = 's0';
|
||||
let currentSection = originalCurrentSection;
|
||||
|
||||
vi.mock('../../utils/settingsParser', () => ({
|
||||
loadSettingsConfig: vi.fn().mockResolvedValue({
|
||||
contents: settingsHoisted
|
||||
})
|
||||
}));
|
||||
|
||||
vi.mock('vue-router', async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
useRoute: () => ({
|
||||
path: `/settings/${currentSection}`
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
const getWrapper = function getWrapper ({ translations = {} } = {}) {
|
||||
return mountComponent(SettingsView, {}, translations, [], {});
|
||||
};
|
||||
|
||||
describe('SettingsView', () => {
|
||||
describe('heading', () => {
|
||||
test('shows correct translation', () => {
|
||||
const translation = 'Settings';
|
||||
const wrapper = getWrapper({
|
||||
translations: {
|
||||
'preferences.settings': translation
|
||||
}
|
||||
});
|
||||
|
||||
const header = wrapper.find('header');
|
||||
expect(header.text()).toBe(translation);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sidebar', () => {
|
||||
test('shows all top-level sections', async () => {
|
||||
const wrapper = getWrapper();
|
||||
|
||||
await flushPromises();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const sidebarItems = wrapper.findAll('.sidebar-sections-list > li');
|
||||
settings.forEach((setting, index) => {
|
||||
const sidebarItem = sidebarItems[index];
|
||||
|
||||
expect(sidebarItem.text()).toBe(setting.i18n);
|
||||
|
||||
const link = sidebarItem.find('*');
|
||||
expect(link.attributes('href')).toBe(`/settings/${setting.name}`);
|
||||
expect(link.classes('active')).toBe(setting.name === currentSection);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('main', () => {
|
||||
describe('correct element', () => {
|
||||
test('shows that it is loading', () => {
|
||||
const wrapper = getWrapper();
|
||||
|
||||
const mainContentLoading = wrapper.find('.main-content--loading');
|
||||
const mainContentLoaded = wrapper.find('.main-content--loaded');
|
||||
const mainContentNoSection = wrapper.find('.main-content--no-section');
|
||||
|
||||
expect(mainContentLoading.exists()).toBe(true);
|
||||
expect(mainContentLoaded.exists()).toBe(false);
|
||||
expect(mainContentNoSection.exists()).toBe(false);
|
||||
});
|
||||
|
||||
test('shows that no section is selected', async () => {
|
||||
currentSection = '';
|
||||
|
||||
const wrapper = getWrapper();
|
||||
|
||||
await flushPromises();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const mainContentLoading = wrapper.find('.main-content--loading');
|
||||
const mainContentLoaded = wrapper.find('.main-content--loaded');
|
||||
const mainContentNoSection = wrapper.find('.main-content--no-section');
|
||||
|
||||
expect(mainContentLoading.exists()).toBe(false);
|
||||
expect(mainContentLoaded.exists()).toBe(false);
|
||||
expect(mainContentNoSection.exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('shows main content', async () => {
|
||||
const wrapper = getWrapper();
|
||||
|
||||
await flushPromises();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const mainContentLoading = wrapper.find('.main-content--loading');
|
||||
const mainContentLoaded = wrapper.find('.main-content--loaded');
|
||||
const mainContentNoSection = wrapper.find('.main-content--no-section');
|
||||
|
||||
expect(mainContentLoading.exists()).toBe(false);
|
||||
expect(mainContentLoaded.exists()).toBe(true);
|
||||
expect(mainContentNoSection.exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
currentSection = originalCurrentSection;
|
||||
});
|
||||
Reference in New Issue
Block a user