mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-05 22:36:05 +02:00
fix custom nav dialog restore default i18n issue
This commit is contained in:
@@ -192,6 +192,7 @@
|
|||||||
type: 'folder',
|
type: 'folder',
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
name: entry.name,
|
name: entry.name,
|
||||||
|
nameKey: entry.nameKey || null,
|
||||||
icon: entry.icon,
|
icon: entry.icon,
|
||||||
items: Array.isArray(entry.items) ? [...entry.items] : []
|
items: Array.isArray(entry.items) ? [...entry.items] : []
|
||||||
};
|
};
|
||||||
@@ -667,6 +668,7 @@
|
|||||||
const entry = localLayout.value.find((e) => e.type === 'folder' && e.id === folderEditor.editingId);
|
const entry = localLayout.value.find((e) => e.type === 'folder' && e.id === folderEditor.editingId);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
entry.name = folderEditor.data.name.trim();
|
entry.name = folderEditor.data.name.trim();
|
||||||
|
entry.nameKey = null;
|
||||||
entry.icon = folderEditor.data.icon?.trim() || DEFAULT_FOLDER_ICON;
|
entry.icon = folderEditor.data.icon?.trim() || DEFAULT_FOLDER_ICON;
|
||||||
localLayout.value = [...localLayout.value];
|
localLayout.value = [...localLayout.value];
|
||||||
}
|
}
|
||||||
@@ -675,6 +677,7 @@
|
|||||||
type: 'folder',
|
type: 'folder',
|
||||||
id: folderEditor.data.id,
|
id: folderEditor.data.id,
|
||||||
name: folderEditor.data.name.trim(),
|
name: folderEditor.data.name.trim(),
|
||||||
|
nameKey: null,
|
||||||
icon: folderEditor.data.icon?.trim() || DEFAULT_FOLDER_ICON,
|
icon: folderEditor.data.icon?.trim() || DEFAULT_FOLDER_ICON,
|
||||||
items: []
|
items: []
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
|
||||||
|
vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: (k) => k }) }));
|
||||||
|
vi.mock('@/shared/utils/common', () => ({ openExternalLink: vi.fn() }));
|
||||||
|
vi.mock('@/components/ui/dialog', () => ({
|
||||||
|
Dialog: { template: '<div><slot /></div>' },
|
||||||
|
DialogContent: { template: '<div><slot /></div>' },
|
||||||
|
DialogFooter: { template: '<div><slot /></div>' },
|
||||||
|
DialogHeader: { template: '<div><slot /></div>' },
|
||||||
|
DialogTitle: { template: '<div><slot /></div>' }
|
||||||
|
}));
|
||||||
|
vi.mock('@/components/ui/button', () => ({
|
||||||
|
Button: {
|
||||||
|
emits: ['click'],
|
||||||
|
template: '<button data-testid="btn" @click="$emit(\'click\')"><slot /></button>'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
vi.mock('@/components/ui/hover-card', () => ({
|
||||||
|
HoverCard: { template: '<div><slot /></div>' },
|
||||||
|
HoverCardContent: { template: '<div><slot /></div>' },
|
||||||
|
HoverCardTrigger: { template: '<div><slot /></div>' }
|
||||||
|
}));
|
||||||
|
vi.mock('@/components/ui/input-group', () => ({
|
||||||
|
InputGroupButton: { template: '<button><slot /></button>' },
|
||||||
|
InputGroupField: { template: '<input />' }
|
||||||
|
}));
|
||||||
|
vi.mock('@/components/ui/separator', () => ({ Separator: { template: '<hr />' } }));
|
||||||
|
vi.mock('@/components/ui/tree', () => ({
|
||||||
|
Tree: {
|
||||||
|
props: ['items'],
|
||||||
|
template: '<div><slot :flatten-items="[]" /></div>'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
vi.mock('@dnd-kit/vue', () => ({ DragDropProvider: { template: '<div><slot /></div>' } }));
|
||||||
|
vi.mock('@dnd-kit/vue/sortable', () => ({ isSortable: () => false }));
|
||||||
|
vi.mock('lucide-vue-next', () => new Proxy({}, { get: () => ({ template: '<i />' }) }));
|
||||||
|
vi.mock('../SortableTreeNode.vue', () => ({ default: { template: '<div />' } }));
|
||||||
|
|
||||||
|
import CustomNavDialog from '../CustomNavDialog.vue';
|
||||||
|
|
||||||
|
describe('CustomNavDialog.vue', () => {
|
||||||
|
it('keeps folder nameKey when restoring defaults and saving', async () => {
|
||||||
|
const defaultLayout = [
|
||||||
|
{
|
||||||
|
type: 'folder',
|
||||||
|
id: 'default-folder-social',
|
||||||
|
nameKey: 'nav_tooltip.social',
|
||||||
|
name: 'Social',
|
||||||
|
icon: 'ri-group-line',
|
||||||
|
items: ['friend-log']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const wrapper = mount(CustomNavDialog, {
|
||||||
|
props: {
|
||||||
|
visible: true,
|
||||||
|
layout: [],
|
||||||
|
hiddenKeys: [],
|
||||||
|
defaultLayout
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const buttons = wrapper.findAll('[data-testid="btn"]');
|
||||||
|
const resetButton = buttons.find((button) => button.text().includes('nav_menu.custom_nav.restore_default'));
|
||||||
|
const saveButton = buttons.find((button) => button.text().includes('common.actions.confirm'));
|
||||||
|
|
||||||
|
await resetButton.trigger('click');
|
||||||
|
await saveButton.trigger('click');
|
||||||
|
|
||||||
|
const emitted = wrapper.emitted('save');
|
||||||
|
expect(emitted).toBeTruthy();
|
||||||
|
expect(emitted[0][0]).toEqual(defaultLayout);
|
||||||
|
expect(emitted[0][0][0].nameKey).toBe('nav_tooltip.social');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => ({
|
||||||
|
avatarRemoteDatabaseProviderList: require('vue').ref([]),
|
||||||
|
saveAvatarProviderList: vi.fn(),
|
||||||
|
removeAvatarProvider: vi.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('pinia', async (importOriginal) => {
|
||||||
|
const actual = await importOriginal();
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
storeToRefs: (store) => store
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock('vue-i18n', () => ({
|
||||||
|
useI18n: () => ({
|
||||||
|
t: (key) => key,
|
||||||
|
locale: require('vue').ref('en')
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../../../stores', () => ({
|
||||||
|
useAvatarProviderStore: () => ({
|
||||||
|
avatarRemoteDatabaseProviderList: mocks.avatarRemoteDatabaseProviderList,
|
||||||
|
saveAvatarProviderList: (...args) => mocks.saveAvatarProviderList(...args),
|
||||||
|
removeAvatarProvider: (...args) => mocks.removeAvatarProvider(...args)
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@/components/ui/dialog', () => ({
|
||||||
|
Dialog: {
|
||||||
|
props: ['open'],
|
||||||
|
emits: ['update:open'],
|
||||||
|
template:
|
||||||
|
'<div data-testid="dialog" v-if="open">' +
|
||||||
|
'<button data-testid="close-dialog" @click="$emit(\'update:open\', false)">close</button>' +
|
||||||
|
'<slot />' +
|
||||||
|
'</div>'
|
||||||
|
},
|
||||||
|
DialogContent: { template: '<div><slot /></div>' },
|
||||||
|
DialogHeader: { template: '<div><slot /></div>' },
|
||||||
|
DialogTitle: { template: '<h2><slot /></h2>' }
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@/components/ui/button', () => ({
|
||||||
|
Button: {
|
||||||
|
emits: ['click'],
|
||||||
|
template: '<button data-testid="add-provider" @click="$emit(\'click\')"><slot /></button>'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@/components/ui/input-group', () => ({
|
||||||
|
InputGroupAction: {
|
||||||
|
props: ['modelValue', 'size'],
|
||||||
|
emits: ['update:modelValue', 'change'],
|
||||||
|
template:
|
||||||
|
'<div class="provider-row">' +
|
||||||
|
'<input data-testid="provider-input" :value="modelValue" @input="$emit(\'update:modelValue\', $event.target.value)" @change="$emit(\'change\')" />' +
|
||||||
|
'<slot name="actions" />' +
|
||||||
|
'</div>'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('lucide-vue-next', () => ({
|
||||||
|
Trash2: {
|
||||||
|
emits: ['click'],
|
||||||
|
template: '<button data-testid="trash" @click="$emit(\'click\')">trash</button>'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
import AvatarProviderDialog from '../AvatarProviderDialog.vue';
|
||||||
|
|
||||||
|
function mountComponent(props = {}) {
|
||||||
|
return mount(AvatarProviderDialog, {
|
||||||
|
props: {
|
||||||
|
isAvatarProviderDialogVisible: true,
|
||||||
|
...props
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('AvatarProviderDialog.vue', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.avatarRemoteDatabaseProviderList.value = ['https://a.example', 'https://b.example'];
|
||||||
|
mocks.saveAvatarProviderList.mockReset();
|
||||||
|
mocks.removeAvatarProvider.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders provider rows when dialog is visible', () => {
|
||||||
|
const wrapper = mountComponent();
|
||||||
|
|
||||||
|
expect(wrapper.find('[data-testid="dialog"]').exists()).toBe(true);
|
||||||
|
expect(wrapper.findAll('.provider-row')).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('emits close when dialog open updates to false', async () => {
|
||||||
|
const wrapper = mountComponent();
|
||||||
|
|
||||||
|
await wrapper.get('[data-testid="close-dialog"]').trigger('click');
|
||||||
|
|
||||||
|
expect(wrapper.emitted('update:isAvatarProviderDialogVisible')).toEqual([[false]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('adds empty provider entry when add button clicked', async () => {
|
||||||
|
const wrapper = mountComponent();
|
||||||
|
|
||||||
|
await wrapper.get('[data-testid="add-provider"]').trigger('click');
|
||||||
|
|
||||||
|
expect(mocks.avatarRemoteDatabaseProviderList.value).toEqual([
|
||||||
|
'https://a.example',
|
||||||
|
'https://b.example',
|
||||||
|
''
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('calls saveAvatarProviderList on provider input change', async () => {
|
||||||
|
const wrapper = mountComponent();
|
||||||
|
|
||||||
|
const input = wrapper.findAll('[data-testid="provider-input"]')[0];
|
||||||
|
await input.setValue('https://updated.example');
|
||||||
|
|
||||||
|
expect(mocks.avatarRemoteDatabaseProviderList.value[0]).toBe('https://updated.example');
|
||||||
|
expect(mocks.saveAvatarProviderList).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('calls removeAvatarProvider with row provider when trash clicked', async () => {
|
||||||
|
const wrapper = mountComponent();
|
||||||
|
|
||||||
|
await wrapper.findAll('[data-testid="trash"]')[1].trigger('click');
|
||||||
|
|
||||||
|
expect(mocks.removeAvatarProvider).toHaveBeenCalledWith('https://b.example');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user