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