mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-03 05:26:05 +02:00
add vrchat servers status to status bar
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { nextTick, ref } from 'vue';
|
||||
|
||||
import StatusBar from '../StatusBar.vue';
|
||||
import en from '../../localization/en.json';
|
||||
|
||||
// --- Mocks ---
|
||||
|
||||
vi.mock('../../service/config', () => ({
|
||||
default: {
|
||||
init: vi.fn(),
|
||||
getString: vi
|
||||
.fn()
|
||||
.mockImplementation((_key, defaultValue) => defaultValue ?? '{}'),
|
||||
setString: vi.fn(),
|
||||
getBool: vi
|
||||
.fn()
|
||||
.mockImplementation((_key, defaultValue) => defaultValue ?? false),
|
||||
setBool: vi.fn(),
|
||||
getInt: vi
|
||||
.fn()
|
||||
.mockImplementation((_key, defaultValue) => defaultValue ?? 0),
|
||||
setInt: vi.fn(),
|
||||
getFloat: vi
|
||||
.fn()
|
||||
.mockImplementation((_key, defaultValue) => defaultValue ?? 0),
|
||||
setFloat: vi.fn(),
|
||||
getObject: vi.fn().mockReturnValue(null),
|
||||
setObject: vi.fn(),
|
||||
getArray: vi.fn().mockReturnValue([]),
|
||||
setArray: vi.fn(),
|
||||
remove: vi.fn()
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../../service/websocket', () => ({
|
||||
wsState: { connected: false, messageCount: 0, bytesReceived: 0 },
|
||||
initWebsocket: vi.fn(),
|
||||
closeWebSocket: vi.fn(),
|
||||
reconnectWebSocket: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('../../service/webapi', () => ({
|
||||
default: {
|
||||
execute: vi.fn().mockResolvedValue({
|
||||
status: 200,
|
||||
data: JSON.stringify({
|
||||
page: { updated_at: '2026-01-01T00:00:00.000Z' },
|
||||
status: { description: 'All Systems Operational' }
|
||||
})
|
||||
})
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('worker-timers', () => ({
|
||||
setInterval: vi.fn(),
|
||||
clearInterval: vi.fn(),
|
||||
setTimeout: vi.fn(),
|
||||
clearTimeout: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('../../service/jsonStorage', () => ({
|
||||
default: vi.fn()
|
||||
}));
|
||||
vi.mock('../../service/watchState', () => ({
|
||||
watchState: { isLoggedIn: false }
|
||||
}));
|
||||
vi.mock('../../service/database', () => ({
|
||||
database: new Proxy(
|
||||
{},
|
||||
{
|
||||
get: (_target, prop) => {
|
||||
if (prop === '__esModule') return false;
|
||||
return vi.fn().mockResolvedValue(null);
|
||||
}
|
||||
}
|
||||
)
|
||||
}));
|
||||
vi.mock('../../plugin/router', () => ({
|
||||
router: {
|
||||
beforeEach: vi.fn(),
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
currentRoute: ref({ path: '/', name: '', meta: {} }),
|
||||
isReady: vi.fn().mockResolvedValue(true)
|
||||
},
|
||||
initRouter: vi.fn()
|
||||
}));
|
||||
vi.mock('vue-router', async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
useRouter: vi.fn(() => ({
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
currentRoute: ref({ path: '/', name: '', meta: {} })
|
||||
}))
|
||||
};
|
||||
});
|
||||
vi.mock('../../plugin/interopApi', () => ({
|
||||
initInteropApi: vi.fn()
|
||||
}));
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'en',
|
||||
fallbackLocale: 'en',
|
||||
legacy: false,
|
||||
globalInjection: false,
|
||||
missingWarn: false,
|
||||
fallbackWarn: false,
|
||||
messages: { en }
|
||||
});
|
||||
|
||||
const stubs = {
|
||||
TooltipWrapper: {
|
||||
template: '<span data-testid="tooltip"><slot /></span>',
|
||||
props: [
|
||||
'content',
|
||||
'disabled',
|
||||
'delayDuration',
|
||||
'delay-duration',
|
||||
'side'
|
||||
]
|
||||
},
|
||||
ContextMenu: { template: '<div><slot /></div>' },
|
||||
ContextMenuTrigger: { template: '<div><slot /></div>' },
|
||||
ContextMenuContent: { template: '<div><slot /></div>' },
|
||||
ContextMenuCheckboxItem: {
|
||||
template: '<div><slot /></div>',
|
||||
props: ['modelValue']
|
||||
},
|
||||
ContextMenuSeparator: { template: '<div />' },
|
||||
ContextMenuSub: { template: '<div><slot /></div>' },
|
||||
ContextMenuSubTrigger: { template: '<div><slot /></div>' },
|
||||
ContextMenuSubContent: { template: '<div><slot /></div>' },
|
||||
ContextMenuRadioGroup: {
|
||||
template: '<div><slot /></div>',
|
||||
props: ['modelValue']
|
||||
},
|
||||
ContextMenuRadioItem: { template: '<div><slot /></div>', props: ['value'] },
|
||||
HoverCard: {
|
||||
template: '<div data-testid="hover-card"><slot /></div>',
|
||||
props: ['open']
|
||||
},
|
||||
HoverCardTrigger: {
|
||||
template: '<div data-testid="hover-card-trigger"><slot /></div>'
|
||||
},
|
||||
HoverCardContent: {
|
||||
template: '<div data-testid="hover-card-content"><slot /></div>',
|
||||
props: ['class', 'side', 'align', 'sideOffset']
|
||||
},
|
||||
Popover: { template: '<div><slot /></div>', props: ['open'] },
|
||||
PopoverTrigger: { template: '<div><slot /></div>' },
|
||||
PopoverContent: {
|
||||
template: '<div><slot /></div>',
|
||||
props: ['class', 'side', 'align']
|
||||
},
|
||||
Select: { template: '<div><slot /></div>', props: ['modelValue'] },
|
||||
SelectTrigger: { template: '<div><slot /></div>', props: ['size'] },
|
||||
SelectValue: { template: '<span />', props: ['placeholder'] },
|
||||
SelectContent: { template: '<div><slot /></div>', props: ['class'] },
|
||||
SelectGroup: { template: '<div><slot /></div>' },
|
||||
SelectItem: { template: '<div><slot /></div>', props: ['value'] },
|
||||
NumberField: {
|
||||
template: '<div><slot /></div>',
|
||||
props: ['modelValue', 'step', 'formatOptions', 'class']
|
||||
},
|
||||
NumberFieldContent: { template: '<div><slot /></div>' },
|
||||
NumberFieldDecrement: { template: '<button />' },
|
||||
NumberFieldIncrement: { template: '<button />' },
|
||||
NumberFieldInput: { template: '<input />', props: ['class'] }
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param storeOverrides
|
||||
*/
|
||||
function mountStatusBar(storeOverrides = {}) {
|
||||
return mount(StatusBar, {
|
||||
global: {
|
||||
plugins: [
|
||||
i18n,
|
||||
createTestingPinia({
|
||||
stubActions: true,
|
||||
initialState: {
|
||||
Game: {
|
||||
isGameRunning: false,
|
||||
isSteamVRRunning: false,
|
||||
...storeOverrides.Game
|
||||
},
|
||||
Vrcx: {
|
||||
proxyServer: '',
|
||||
appStartAt: Date.now(),
|
||||
...storeOverrides.Vrcx
|
||||
},
|
||||
VrcStatus: {
|
||||
lastStatus: '',
|
||||
lastStatusTime: null,
|
||||
lastStatusSummary: '',
|
||||
...storeOverrides.VrcStatus
|
||||
},
|
||||
GeneralSettings: {
|
||||
...storeOverrides.GeneralSettings
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
stubs
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('StatusBar.vue - Servers indicator', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('shows "Game" label instead of "VRChat" for game running indicator', () => {
|
||||
const wrapper = mountStatusBar({ Game: { isGameRunning: true } });
|
||||
expect(wrapper.text()).toContain('Game');
|
||||
});
|
||||
|
||||
test('shows Servers indicator with green dot when no issues', () => {
|
||||
const wrapper = mountStatusBar();
|
||||
expect(wrapper.text()).toContain('Servers');
|
||||
const serversDots = wrapper.findAll(
|
||||
'.bg-\\[var\\(--status-online\\)\\]'
|
||||
);
|
||||
expect(serversDots.length).toBeGreaterThan(0);
|
||||
expect(wrapper.find('.bg-\\[\\#e6a23c\\]').exists()).toBe(false);
|
||||
});
|
||||
|
||||
test('shows Servers indicator with yellow dot when there is an issue', () => {
|
||||
const wrapper = mountStatusBar({
|
||||
VrcStatus: {
|
||||
lastStatus: 'Partial System Outage'
|
||||
}
|
||||
});
|
||||
expect(wrapper.text()).toContain('Servers');
|
||||
expect(wrapper.find('.bg-\\[\\#e6a23c\\]').exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('shows HoverCard content with status text when there is an issue', () => {
|
||||
const wrapper = mountStatusBar({
|
||||
VrcStatus: {
|
||||
lastStatus: 'Partial System Outage',
|
||||
lastStatusSummary: 'API, CDN'
|
||||
}
|
||||
});
|
||||
const hoverContent = wrapper.find('[data-testid="hover-card-content"]');
|
||||
expect(hoverContent.exists()).toBe(true);
|
||||
expect(hoverContent.text()).toContain('VRChat Server Issues');
|
||||
});
|
||||
|
||||
test('does not show HoverCard content when no issues', () => {
|
||||
const wrapper = mountStatusBar();
|
||||
const hoverContent = wrapper.find('[data-testid="hover-card-content"]');
|
||||
expect(hoverContent.exists()).toBe(false);
|
||||
});
|
||||
|
||||
test('shows Servers indicator in context menu', () => {
|
||||
const wrapper = mountStatusBar();
|
||||
const text = wrapper.text();
|
||||
expect(text).toContain('Servers');
|
||||
});
|
||||
|
||||
test('shows SteamVR indicator', () => {
|
||||
const wrapper = mountStatusBar({ Game: { isSteamVRRunning: true } });
|
||||
expect(wrapper.text()).toContain('SteamVR');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user