mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-05 16:21:59 +02:00
fix test
This commit is contained in:
@@ -211,6 +211,7 @@
|
||||
:step="1"
|
||||
:format-options="{ maximumFractionDigits: 0 }"
|
||||
class="w-20"
|
||||
@click.stop
|
||||
@update:modelValue="setZoomLevel">
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
|
||||
@@ -236,7 +236,7 @@ describe('StatusBar.vue - Servers indicator', () => {
|
||||
expect(wrapper.text()).toContain('Servers');
|
||||
const serversDots = wrapper.findAll('.bg-status-online');
|
||||
expect(serversDots.length).toBeGreaterThan(0);
|
||||
expect(wrapper.find('.bg-\\[\\#e6a23c\\]').exists()).toBe(false);
|
||||
expect(wrapper.find('.bg-status-askme').exists()).toBe(false);
|
||||
});
|
||||
|
||||
test('shows Servers indicator with yellow dot when there is an issue', () => {
|
||||
@@ -246,7 +246,7 @@ describe('StatusBar.vue - Servers indicator', () => {
|
||||
}
|
||||
});
|
||||
expect(wrapper.text()).toContain('Servers');
|
||||
expect(wrapper.find('.bg-\\[\\#e6a23c\\]').exists()).toBe(true);
|
||||
expect(wrapper.find('.bg-status-askme').exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('shows HoverCard content with status text when there is an issue', () => {
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import { nextTick, ref } from 'vue';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
timeToText: vi.fn((ms) => `${ms}ms`)
|
||||
timeToText: vi.fn((ms) => `${ms}ms`),
|
||||
nowRef: null
|
||||
}));
|
||||
|
||||
vi.mock('../../shared/utils', () => ({
|
||||
timeToText: (...args) => mocks.timeToText(...args)
|
||||
}));
|
||||
|
||||
vi.mock('@vueuse/core', () => ({
|
||||
useNow: () => mocks.nowRef
|
||||
}));
|
||||
|
||||
import Timer from '../Timer.vue';
|
||||
|
||||
describe('Timer.vue', () => {
|
||||
let intervalCallback;
|
||||
|
||||
beforeEach(() => {
|
||||
intervalCallback = null;
|
||||
mocks.timeToText.mockClear();
|
||||
|
||||
vi.spyOn(globalThis, 'setInterval').mockImplementation((cb) => {
|
||||
intervalCallback = cb;
|
||||
return 99;
|
||||
});
|
||||
vi.spyOn(globalThis, 'clearInterval').mockImplementation(() => {});
|
||||
vi.spyOn(Date, 'now').mockReturnValue(10000);
|
||||
mocks.nowRef = ref(10000);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -42,15 +38,14 @@ describe('Timer.vue', () => {
|
||||
expect(mocks.timeToText).toHaveBeenCalledWith(6000);
|
||||
});
|
||||
|
||||
it('updates text when interval callback runs', async () => {
|
||||
it('updates text when now value changes', async () => {
|
||||
const wrapper = mount(Timer, {
|
||||
props: {
|
||||
epoch: 4000
|
||||
}
|
||||
});
|
||||
|
||||
vi.mocked(Date.now).mockReturnValue(13000);
|
||||
intervalCallback?.();
|
||||
mocks.nowRef.value = 13000;
|
||||
await nextTick();
|
||||
|
||||
expect(wrapper.text()).toBe('9000ms');
|
||||
@@ -66,15 +61,15 @@ describe('Timer.vue', () => {
|
||||
expect(wrapper.text()).toBe('-');
|
||||
});
|
||||
|
||||
it('clears interval on unmount', () => {
|
||||
it('computes correct elapsed time', () => {
|
||||
mocks.nowRef.value = 20000;
|
||||
const wrapper = mount(Timer, {
|
||||
props: {
|
||||
epoch: 1
|
||||
epoch: 5000
|
||||
}
|
||||
});
|
||||
|
||||
wrapper.unmount();
|
||||
|
||||
expect(clearInterval).toHaveBeenCalledWith(99);
|
||||
expect(wrapper.text()).toBe('15000ms');
|
||||
expect(mocks.timeToText).toHaveBeenCalledWith(15000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,6 +38,10 @@ vi.mock('../../../../coordinators/imageUploadCoordinator', () => ({
|
||||
uploadImageLegacy: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('../../../../coordinators/avatarCoordinator', () => ({
|
||||
removeAvatarFromCache: vi.fn()
|
||||
}));
|
||||
|
||||
const { copyToClipboard, openExternalLink } =
|
||||
await import('../../../../shared/utils');
|
||||
const { favoriteRequest, avatarRequest, avatarModerationRequest } =
|
||||
|
||||
@@ -27,11 +27,15 @@ vi.mock('../../../../stores', () => ({
|
||||
vi.mock('../../../../composables/useInviteChecks', () => ({
|
||||
useInviteChecks: () => ({ checkCanInvite: () => true })
|
||||
}));
|
||||
vi.mock('../../../../composables/useRecentActions', () => ({
|
||||
isActionRecent: () => false
|
||||
}));
|
||||
vi.mock('../../../ui/dropdown-menu', () => ({
|
||||
DropdownMenu: { template: '<div><slot /></div>' },
|
||||
DropdownMenuTrigger: { template: '<div><slot /></div>' },
|
||||
DropdownMenuContent: { template: '<div><slot /></div>' },
|
||||
DropdownMenuSeparator: { template: '<hr />' },
|
||||
DropdownMenuShortcut: { template: '<span><slot /></span>' },
|
||||
DropdownMenuItem: {
|
||||
emits: ['click'],
|
||||
template:
|
||||
@@ -51,6 +55,7 @@ vi.mock('../../../ui/tooltip', () => ({
|
||||
vi.mock('lucide-vue-next', () => ({
|
||||
Check: { template: '<i />' },
|
||||
CheckCircle: { template: '<i />' },
|
||||
Clock: { template: '<i />' },
|
||||
Flag: { template: '<i />' },
|
||||
LineChart: { template: '<i />' },
|
||||
Mail: { template: '<i />' },
|
||||
|
||||
@@ -223,13 +223,13 @@ describe('UserDialogAvatarsTab.vue', () => {
|
||||
expect(input.exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('does not render search input for other users', () => {
|
||||
test('renders search input for other users too', () => {
|
||||
const wrapper = mountComponent({
|
||||
id: 'usr_other',
|
||||
ref: { id: 'usr_other' }
|
||||
});
|
||||
const input = wrapper.find('input');
|
||||
expect(input.exists()).toBe(false);
|
||||
expect(input.exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('filters avatars by search query', async () => {
|
||||
|
||||
@@ -39,6 +39,13 @@ vi.mock('../../../../services/database', () => ({
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../../../../composables/useRecentActions', () => ({
|
||||
recordRecentAction: vi.fn(),
|
||||
useRecentActions: () => ({
|
||||
isRecentAction: vi.fn(() => false)
|
||||
})
|
||||
}));
|
||||
|
||||
// Import mocks after vi.mock
|
||||
const { copyToClipboard } = await import('../../../../shared/utils');
|
||||
const {
|
||||
|
||||
@@ -36,6 +36,10 @@ vi.mock('../../../../coordinators/imageUploadCoordinator', () => ({
|
||||
uploadImageLegacy: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('../../../../coordinators/worldCoordinator', () => ({
|
||||
removeWorldFromCache: vi.fn()
|
||||
}));
|
||||
|
||||
const { favoriteRequest, miscRequest, userRequest, worldRequest } =
|
||||
await import('../../../../api');
|
||||
const { openExternalLink } = await import('../../../../shared/utils');
|
||||
|
||||
@@ -84,7 +84,8 @@ describe('NavMenuFolderItem', () => {
|
||||
hasNotifications: false,
|
||||
isEntryNotified: () => false,
|
||||
isNavItemNotified: () => false,
|
||||
isDashboardItem: () => false
|
||||
isDashboardItem: () => false,
|
||||
isToolItem: () => false
|
||||
}
|
||||
});
|
||||
|
||||
@@ -103,7 +104,8 @@ describe('NavMenuFolderItem', () => {
|
||||
hasNotifications: false,
|
||||
isEntryNotified: () => false,
|
||||
isNavItemNotified: () => false,
|
||||
isDashboardItem: () => false
|
||||
isDashboardItem: () => false,
|
||||
isToolItem: () => false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -92,7 +92,9 @@ describe('useNavLayout', () => {
|
||||
await applyCustomNavLayout(layout, []);
|
||||
await nextTick();
|
||||
|
||||
expect(navLayout.value).toEqual(layout);
|
||||
expect(navLayout.value).toEqual(
|
||||
expect.arrayContaining(layout)
|
||||
);
|
||||
expect(mocks.setString).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { computed, reactive, ref, shallowReactive, watch } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
import {
|
||||
compareByCreatedAt,
|
||||
@@ -33,7 +33,7 @@ export const useUserStore = defineStore('User', () => {
|
||||
const locationStore = useLocationStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
const uiStore = useUiStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
const currentUser = ref({
|
||||
acceptedPrivacyVersion: 0,
|
||||
|
||||
@@ -119,6 +119,7 @@ vi.mock('@/components/ui/dropdown-menu', () => ({
|
||||
|
||||
vi.mock('lucide-vue-next', () => ({
|
||||
AlertTriangle: { template: '<i />' },
|
||||
Image: { template: '<i />' },
|
||||
Lock: { template: '<i />' },
|
||||
MoreHorizontal: { template: '<i />' },
|
||||
Trash2: { template: '<i />' }
|
||||
@@ -207,7 +208,7 @@ describe('FavoritesAvatarItem.vue', () => {
|
||||
).toContain('rounded-sm');
|
||||
});
|
||||
|
||||
it('shows fallback text when thumbnail is missing', () => {
|
||||
it('shows fallback icon when thumbnail is missing', () => {
|
||||
const wrapper = mountItem({
|
||||
favorite: {
|
||||
id: 'avtr_no_thumb',
|
||||
@@ -223,9 +224,9 @@ describe('FavoritesAvatarItem.vue', () => {
|
||||
expect(wrapper.find('[data-testid="avatar-image"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
expect(wrapper.get('[data-testid="avatar-fallback"]').text()).toContain(
|
||||
'B'
|
||||
);
|
||||
expect(
|
||||
wrapper.get('[data-testid="avatar-fallback"]').find('i').exists()
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('uses local delete flow for local favorites', async () => {
|
||||
|
||||
@@ -96,6 +96,7 @@ vi.mock('@/components/ui/dropdown-menu', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('lucide-vue-next', () => ({
|
||||
Image: { template: '<i />' },
|
||||
MoreHorizontal: { template: '<i />' }
|
||||
}));
|
||||
|
||||
@@ -173,7 +174,7 @@ describe('FavoritesAvatarLocalHistoryItem.vue', () => {
|
||||
).toContain('rounded-sm');
|
||||
});
|
||||
|
||||
it('shows fallback text when thumbnail is missing', () => {
|
||||
it('shows fallback icon when thumbnail is missing', () => {
|
||||
const wrapper = mountItem({
|
||||
favorite: {
|
||||
id: 'avtr_hist_no_thumb',
|
||||
@@ -185,9 +186,9 @@ describe('FavoritesAvatarLocalHistoryItem.vue', () => {
|
||||
expect(wrapper.find('[data-testid="avatar-image"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
expect(wrapper.get('[data-testid="avatar-fallback"]').text()).toContain(
|
||||
'C'
|
||||
);
|
||||
expect(
|
||||
wrapper.get('[data-testid="avatar-fallback"]').find('i').exists()
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('runs select-avatar action from menu', async () => {
|
||||
|
||||
@@ -177,7 +177,8 @@ vi.mock('@/components/ui/dropdown-menu', () => ({
|
||||
|
||||
vi.mock('lucide-vue-next', () => ({
|
||||
MoreHorizontal: { template: '<i />' },
|
||||
Trash2: { template: '<i />' }
|
||||
Trash2: { template: '<i />' },
|
||||
User: { template: '<i />' }
|
||||
}));
|
||||
|
||||
import FavoritesFriendItem from '../FavoritesFriendItem.vue';
|
||||
|
||||
@@ -109,6 +109,7 @@ vi.mock('@/components/ui/checkbox', () => ({
|
||||
|
||||
vi.mock('lucide-vue-next', () => ({
|
||||
AlertTriangle: { template: '<i />' },
|
||||
Image: { template: '<i />' },
|
||||
Lock: { template: '<i />' },
|
||||
MoreHorizontal: { template: '<i />' }
|
||||
}));
|
||||
|
||||
@@ -196,6 +196,11 @@ const i18n = createI18n({
|
||||
messages: { en }
|
||||
});
|
||||
|
||||
vi.mock('lucide-vue-next', () => ({
|
||||
Pencil: { template: '<span class="pencil-icon" />' },
|
||||
User: { template: '<span class="user-icon" />' }
|
||||
}));
|
||||
|
||||
// Stub all complex UI components — render slots transparently
|
||||
const stubs = {
|
||||
ContextMenu: { template: '<div data-testid="context-menu"><slot /></div>' },
|
||||
@@ -229,6 +234,7 @@ const stubs = {
|
||||
props: ['location', 'traveling', 'link', 'class']
|
||||
},
|
||||
Pencil: { template: '<span class="pencil-icon" />', props: ['class'] },
|
||||
User: { template: '<span class="user-icon" />', props: ['class', 'size'] },
|
||||
TooltipWrapper: {
|
||||
template: '<span><slot /></span>',
|
||||
props: ['content', 'disabled', 'delayDuration', 'side']
|
||||
@@ -356,11 +362,11 @@ describe('FriendsLocationsCard.vue', () => {
|
||||
expect(wrapper.text()).toContain('A');
|
||||
});
|
||||
|
||||
test('shows ? as avatar fallback when name is empty', () => {
|
||||
test('shows user icon as avatar fallback when name is empty', () => {
|
||||
const wrapper = mountCard({
|
||||
friend: makeFriend({ name: undefined })
|
||||
});
|
||||
expect(wrapper.text()).toContain('?');
|
||||
expect(wrapper.find('.user-icon').exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('hides location when displayInstanceInfo is false', () => {
|
||||
|
||||
@@ -9,9 +9,13 @@ const mocks = vi.hoisted(() => ({
|
||||
}));
|
||||
|
||||
vi.mock('pinia', async (i) => ({ ...(await i()), storeToRefs: (s) => s }));
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => ({ replace: (...a) => mocks.replace(...a) })
|
||||
}));
|
||||
vi.mock('vue-router', async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
useRouter: () => ({ replace: (...a) => mocks.replace(...a) })
|
||||
};
|
||||
});
|
||||
vi.mock('../../../services/watchState', () => ({
|
||||
watchState: { isLoggedIn: false }
|
||||
}));
|
||||
|
||||
@@ -44,7 +44,14 @@ vi.mock('../../../stores', () => ({
|
||||
photonLoggingEnabled: mocks.photonLoggingEnabled,
|
||||
chatboxUserBlacklist: mocks.chatboxUserBlacklist,
|
||||
saveChatboxUserBlacklist: (...args) =>
|
||||
mocks.saveChatboxUserBlacklist(...args)
|
||||
mocks.saveChatboxUserBlacklist(...args),
|
||||
photonEventTable: ref({ data: [], pageSize: 10 }),
|
||||
photonEventTablePrevious: ref({ data: [], pageSize: 10 }),
|
||||
photonEventTableTypeFilter: ref([]),
|
||||
photonEventTableFilter: ref(''),
|
||||
photonEventIcon: ref(false),
|
||||
photonEventTableFilterChange: vi.fn(),
|
||||
showUserFromPhotonId: vi.fn()
|
||||
}),
|
||||
useUserStore: () => ({
|
||||
currentUser: mocks.currentUser
|
||||
@@ -65,6 +72,18 @@ vi.mock('../../../stores', () => ({
|
||||
useGalleryStore: () => ({
|
||||
showFullscreenImageDialog: (...args) =>
|
||||
mocks.showFullscreenImageDialog(...args)
|
||||
}),
|
||||
useSearchStore: () => ({
|
||||
stringComparer: { value: (a, b) => a.localeCompare(b) }
|
||||
}),
|
||||
useAvatarStore: () => ({
|
||||
showAvatarDialog: vi.fn()
|
||||
}),
|
||||
useGroupStore: () => ({
|
||||
showGroupDialog: vi.fn()
|
||||
}),
|
||||
useVrcxStore: () => ({
|
||||
ipcEnabled: ref(false)
|
||||
})
|
||||
}));
|
||||
|
||||
@@ -142,12 +161,14 @@ vi.mock('../dialogs/ChatboxBlacklistDialog.vue', () => ({
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('lucide-vue-next', () => ({
|
||||
Apple: { template: '<span />' },
|
||||
Home: { template: '<span />' },
|
||||
Monitor: { template: '<span />' },
|
||||
Smartphone: { template: '<span />' }
|
||||
}));
|
||||
vi.mock('lucide-vue-next', async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
const stubs = {};
|
||||
for (const key of Object.keys(actual)) {
|
||||
stubs[key] = { template: '<span />' };
|
||||
}
|
||||
return stubs;
|
||||
});
|
||||
|
||||
import PlayerList from '../PlayerList.vue';
|
||||
|
||||
|
||||
@@ -33,12 +33,23 @@ vi.mock('../../../../../stores', () => ({
|
||||
})
|
||||
}));
|
||||
|
||||
vi.mock('../../SimpleSwitch.vue', () => ({
|
||||
default: {
|
||||
props: ['label', 'disabled'],
|
||||
emits: ['change'],
|
||||
vi.mock('@/components/ui/switch', () => ({
|
||||
Switch: {
|
||||
props: ['modelValue', 'disabled'],
|
||||
emits: ['update:modelValue'],
|
||||
template:
|
||||
'<div data-testid="simple-switch" :data-label="label" :data-disabled="disabled"><button class="emit-change" @click="$emit(\'change\', true)" /></div>'
|
||||
'<button data-testid="switch" :data-disabled="disabled || undefined" @click="$emit(\'update:modelValue\', !modelValue)"><slot /></button>'
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../../SettingsGroup.vue', () => ({
|
||||
default: { template: '<div><slot /><slot name="description" /></div>' }
|
||||
}));
|
||||
|
||||
vi.mock('../../SettingsItem.vue', () => ({
|
||||
default: {
|
||||
props: ['label', 'description'],
|
||||
template: '<div :data-label="label"><slot /></div>'
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -57,7 +68,7 @@ describe('DiscordPresenceTab.vue', () => {
|
||||
const wrapper = mount(DiscordPresenceTab);
|
||||
|
||||
const tooltipRow = wrapper
|
||||
.findAll('div.options-container-item')
|
||||
.findAll('p')
|
||||
.find((node) =>
|
||||
node
|
||||
.text()
|
||||
@@ -65,11 +76,13 @@ describe('DiscordPresenceTab.vue', () => {
|
||||
'view.settings.discord_presence.discord_presence.enable_tooltip'
|
||||
)
|
||||
);
|
||||
expect(tooltipRow).toBeTruthy();
|
||||
await tooltipRow.trigger('click');
|
||||
|
||||
expect(mocks.showVRChatConfig).toHaveBeenCalledTimes(1);
|
||||
|
||||
await wrapper.findAll('.emit-change')[0].trigger('click');
|
||||
const switches = wrapper.findAll('[data-testid="switch"]');
|
||||
await switches[0].trigger('click');
|
||||
expect(mocks.discordStore.setDiscordActive).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.discordStore.saveDiscordOption).toHaveBeenCalled();
|
||||
});
|
||||
@@ -78,12 +91,9 @@ describe('DiscordPresenceTab.vue', () => {
|
||||
mocks.discordStore.discordActive.value = false;
|
||||
const wrapper = mount(DiscordPresenceTab);
|
||||
|
||||
const worldIntegration = wrapper
|
||||
.findAll('[data-testid="simple-switch"]')
|
||||
.find((node) =>
|
||||
node.attributes('data-label')?.includes('world_integration')
|
||||
);
|
||||
const switches = wrapper.findAll('[data-testid="switch"]');
|
||||
const worldIntegrationSwitch = switches[1];
|
||||
|
||||
expect(worldIntegration?.attributes('data-disabled')).toBe('true');
|
||||
expect(worldIntegrationSwitch?.attributes('data-disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -53,6 +53,15 @@ vi.mock('@/components/ui/button', () => ({
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/switch', () => ({
|
||||
Switch: {
|
||||
props: ['modelValue', 'disabled'],
|
||||
emits: ['update:modelValue'],
|
||||
template:
|
||||
'<button data-testid="switch" :data-disabled="disabled || undefined" @click="$emit(\'update:modelValue\', !modelValue)"><slot /></button>'
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../../../../components/ui/radio-group', () => ({
|
||||
RadioGroup: {
|
||||
props: ['modelValue', 'disabled'],
|
||||
@@ -72,13 +81,12 @@ vi.mock('../../../../components/ui/toggle-group', () => ({
|
||||
ToggleGroupItem: { template: '<div><slot /></div>' }
|
||||
}));
|
||||
|
||||
vi.mock('../SimpleSwitch.vue', () => ({
|
||||
default: {
|
||||
props: ['label'],
|
||||
emits: ['change'],
|
||||
template:
|
||||
'<div data-testid="simple-switch" :data-label="label"><button class="emit-change" @click="$emit(\'change\', true)" /></div>'
|
||||
}
|
||||
vi.mock('../SettingsGroup.vue', () => ({
|
||||
default: { template: '<div><slot /><slot name="description" /></div>' }
|
||||
}));
|
||||
|
||||
vi.mock('../SettingsItem.vue', () => ({
|
||||
default: { template: '<div><slot /></div>' }
|
||||
}));
|
||||
|
||||
import WristOverlaySettings from '../WristOverlaySettings.vue';
|
||||
@@ -102,7 +110,8 @@ describe('WristOverlaySettings.vue', () => {
|
||||
await wrapper.get('[data-testid="filters-btn"]').trigger('click');
|
||||
expect(wrapper.emitted('open-feed-filters')).toBeTruthy();
|
||||
|
||||
await wrapper.findAll('.emit-change')[0].trigger('click');
|
||||
const switches = wrapper.findAll('[data-testid="switch"]');
|
||||
await switches[0].trigger('click');
|
||||
expect(mocks.notificationsStore.setOpenVR).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.saveOpenVROption).toHaveBeenCalled();
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ const mocks = vi.hoisted(() => ({
|
||||
appearanceStore: {
|
||||
isSidebarGroupByInstance: { value: false },
|
||||
isHideFriendsInSameInstance: { value: false },
|
||||
isSameInstanceAboveFavorites: { value: false },
|
||||
isSidebarDivideByFriendGroup: { value: false },
|
||||
sidebarFavoriteGroups: { value: [] },
|
||||
sidebarFavoriteGroupOrder: { value: [] },
|
||||
|
||||
@@ -39,21 +39,22 @@ vi.mock('pinia', async (importOriginal) => {
|
||||
|
||||
vi.mock('../../../stores', () => ({
|
||||
useFriendStore: () => ({ friends }),
|
||||
useGalleryStore: () => ({ showGalleryPage })
|
||||
}));
|
||||
|
||||
vi.mock('../../../stores/settings/advanced', () => ({
|
||||
useAdvancedSettingsStore: () => ({ showVRChatConfig })
|
||||
}));
|
||||
|
||||
vi.mock('../../../stores/launch', () => ({
|
||||
useLaunchStore: () => ({ showLaunchOptions })
|
||||
}));
|
||||
|
||||
vi.mock('../../../stores/vrcx', () => ({
|
||||
useGalleryStore: () => ({ showGalleryPage }),
|
||||
useToolsStore: () => ({ openDialog: vi.fn() }),
|
||||
useAdvancedSettingsStore: () => ({ showVRChatConfig }),
|
||||
useLaunchStore: () => ({ showLaunchOptions }),
|
||||
useVrcxStore: () => ({ showRegistryBackupDialog })
|
||||
}));
|
||||
|
||||
vi.mock('../../../composables/useToolNavPinning', () => ({
|
||||
useToolNavPinning: () => ({
|
||||
pinToolToNav: vi.fn(),
|
||||
pinnedToolKeys: new Set(),
|
||||
refreshPinnedState: vi.fn().mockResolvedValue(undefined),
|
||||
unpinToolFromNav: vi.fn()
|
||||
})
|
||||
}));
|
||||
|
||||
vi.mock('../../../services/config.js', () => ({
|
||||
default: {
|
||||
getString: (...args) => getString(...args),
|
||||
@@ -65,6 +66,13 @@ vi.mock('../dialogs/AutoChangeStatusDialog.vue', () => ({
|
||||
default: { template: '<div />' }
|
||||
}));
|
||||
|
||||
vi.mock('../../../components/ui/tooltip', () => ({
|
||||
TooltipWrapper: {
|
||||
template: '<div><slot /></div>',
|
||||
props: ['content', 'disabled', 'side']
|
||||
}
|
||||
}));
|
||||
|
||||
import Tools from '../Tools.vue';
|
||||
|
||||
function findToolItemByTitle(wrapper, titleKey) {
|
||||
@@ -113,7 +121,7 @@ describe('Tools.vue', () => {
|
||||
expect(galleryItem).toBeTruthy();
|
||||
await galleryItem.trigger('click');
|
||||
|
||||
expect(showGalleryPage).toHaveBeenCalled();
|
||||
expect(push).toHaveBeenCalledWith({ name: 'gallery' });
|
||||
});
|
||||
|
||||
test('toggle category persists collapsed state', async () => {
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
|
||||
import ToolItem from '../ToolItem.vue';
|
||||
|
||||
describe('ToolItem.vue', () => {
|
||||
test('renders icon, title and description', () => {
|
||||
const MockIcon = defineComponent({
|
||||
template: '<svg data-test="mock-icon" />'
|
||||
});
|
||||
|
||||
const wrapper = mount(ToolItem, {
|
||||
props: {
|
||||
icon: markRaw(MockIcon),
|
||||
icon: 'ri-screenshot-line',
|
||||
title: 'Test title',
|
||||
description: 'Test description'
|
||||
}
|
||||
});
|
||||
|
||||
expect(wrapper.find('[data-test="mock-icon"]').exists()).toBe(true);
|
||||
expect(wrapper.find('i.ri-screenshot-line').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('Test title');
|
||||
expect(wrapper.text()).toContain('Test description');
|
||||
});
|
||||
@@ -28,7 +23,7 @@ describe('ToolItem.vue', () => {
|
||||
|
||||
const wrapper = mount(ToolItem, {
|
||||
props: {
|
||||
icon: markRaw(defineComponent({ template: '<svg />' })),
|
||||
icon: 'ri-screenshot-line',
|
||||
title: 'Clickable title',
|
||||
description: 'Clickable description'
|
||||
},
|
||||
|
||||
@@ -49,6 +49,19 @@ Object.defineProperty(window, 'matchMedia', {
|
||||
}))
|
||||
});
|
||||
|
||||
// localStorage polyfill (jsdom may not provide a full implementation)
|
||||
if (typeof globalThis.localStorage === 'undefined' || typeof globalThis.localStorage.clear !== 'function') {
|
||||
const store = new Map();
|
||||
globalThis.localStorage = {
|
||||
getItem: (key) => store.get(key) ?? null,
|
||||
setItem: (key, value) => store.set(key, String(value)),
|
||||
removeItem: (key) => store.delete(key),
|
||||
clear: () => store.clear(),
|
||||
get length() { return store.size; },
|
||||
key: (index) => [...store.keys()][index] ?? null
|
||||
};
|
||||
}
|
||||
|
||||
// Notification API stub
|
||||
globalThis.Notification = class {
|
||||
static permission = 'denied';
|
||||
|
||||
Reference in New Issue
Block a user