mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-22 16:23:50 +02:00
refactor
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
import { describe, expect, test, vi, beforeEach } from 'vitest';
|
||||
import { ref } from 'vue';
|
||||
|
||||
vi.mock('../../../../api', () => ({
|
||||
groupRequest: {
|
||||
getGroupMembersSearch: vi.fn(),
|
||||
getCachedGroupMember: vi.fn(),
|
||||
getCachedGroupMembers: vi.fn()
|
||||
},
|
||||
userRequest: {}
|
||||
}));
|
||||
vi.mock('../../../../plugin/router', () => {
|
||||
const { ref } = require('vue');
|
||||
return {
|
||||
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();
|
||||
const { ref } = require('vue');
|
||||
return {
|
||||
...actual,
|
||||
useRouter: vi.fn(() => ({
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
currentRoute: ref({ path: '/', name: '', meta: {} })
|
||||
}))
|
||||
};
|
||||
});
|
||||
vi.mock('../../../../plugin/interopApi', () => ({ initInteropApi: vi.fn() }));
|
||||
vi.mock('../../../../service/database', () => ({
|
||||
database: new Proxy(
|
||||
{},
|
||||
{
|
||||
get: (_target, prop) => {
|
||||
if (prop === '__esModule') return false;
|
||||
return vi.fn().mockResolvedValue(null);
|
||||
}
|
||||
}
|
||||
)
|
||||
}));
|
||||
vi.mock('../../../../service/config', () => ({
|
||||
default: {
|
||||
init: vi.fn(),
|
||||
getString: vi.fn().mockImplementation((_k, d) => d ?? '{}'),
|
||||
setString: vi.fn(),
|
||||
getBool: vi.fn().mockImplementation((_k, d) => d ?? false),
|
||||
setBool: vi.fn(),
|
||||
getInt: vi.fn().mockImplementation((_k, d) => d ?? 0),
|
||||
setInt: vi.fn(),
|
||||
getFloat: vi.fn().mockImplementation((_k, d) => d ?? 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/jsonStorage', () => ({ default: vi.fn() }));
|
||||
vi.mock('../../../../service/watchState', () => ({
|
||||
watchState: { isLoggedIn: false }
|
||||
}));
|
||||
vi.mock('../../../../service/request', () => ({
|
||||
request: vi.fn().mockResolvedValue({ json: {} }),
|
||||
processBulk: vi.fn(),
|
||||
buildRequestInit: vi.fn(),
|
||||
parseResponse: vi.fn(),
|
||||
shouldIgnoreError: vi.fn(),
|
||||
$throw: vi.fn(),
|
||||
failedGetRequests: new Map()
|
||||
}));
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: () => ({
|
||||
t: (key) => key
|
||||
}),
|
||||
createI18n: () => ({
|
||||
global: { t: (key) => key },
|
||||
install: vi.fn()
|
||||
})
|
||||
}));
|
||||
vi.mock('worker-timers', () => ({
|
||||
setTimeout: (fn, ms) => globalThis.setTimeout(fn, ms),
|
||||
clearTimeout: (id) => globalThis.clearTimeout(id)
|
||||
}));
|
||||
|
||||
import { useGroupMembers } from '../useGroupMembers';
|
||||
import { groupRequest } from '../../../../api';
|
||||
import { groupDialogFilterOptions } from '../../../../shared/constants';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param overrides
|
||||
*/
|
||||
function createGroupDialog(overrides = {}) {
|
||||
return ref({
|
||||
id: 'grp_1',
|
||||
visible: true,
|
||||
inGroup: false,
|
||||
members: [],
|
||||
memberSearch: '',
|
||||
memberSearchResults: [],
|
||||
memberSortOrder: { value: '' },
|
||||
memberFilter: { id: null, name: 'Everyone' },
|
||||
ref: { roles: [], memberCount: 0 },
|
||||
...overrides
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param overrides
|
||||
*/
|
||||
function createDeps(overrides = {}) {
|
||||
return {
|
||||
currentUser: ref({ id: 'usr_me' }),
|
||||
applyGroupMember: vi.fn((json) => json),
|
||||
handleGroupMember: vi.fn(),
|
||||
t: (key) => key,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
describe('useGroupMembers', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
groupRequest.getCachedGroupMembers.mockReset();
|
||||
});
|
||||
|
||||
describe('groupDialogMemberSortValue', () => {
|
||||
test('returns current sort order value', () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
memberSortOrder: { value: 'joinedAt:desc', name: 'sort.joined' }
|
||||
});
|
||||
const { groupDialogMemberSortValue } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
expect(groupDialogMemberSortValue.value).toBe('joinedAt:desc');
|
||||
});
|
||||
|
||||
test('returns empty string when no sort order', () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
memberSortOrder: {}
|
||||
});
|
||||
const { groupDialogMemberSortValue } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
expect(groupDialogMemberSortValue.value).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('groupDialogMemberFilterKey', () => {
|
||||
test('returns everyone when filter id is null', () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
memberFilter: { id: null }
|
||||
});
|
||||
const { groupDialogMemberFilterKey } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
expect(groupDialogMemberFilterKey.value).toBe('everyone');
|
||||
});
|
||||
|
||||
test('returns usersWithNoRole when filter id is empty string', () => {
|
||||
const groupDialog = createGroupDialog({ memberFilter: { id: '' } });
|
||||
const { groupDialogMemberFilterKey } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
expect(groupDialogMemberFilterKey.value).toBe('usersWithNoRole');
|
||||
});
|
||||
|
||||
test('returns role:id for role-based filters', () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
memberFilter: { id: 'role_123' }
|
||||
});
|
||||
const { groupDialogMemberFilterKey } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
expect(groupDialogMemberFilterKey.value).toBe('role:role_123');
|
||||
});
|
||||
|
||||
test('returns null when no filter', () => {
|
||||
const groupDialog = createGroupDialog({ memberFilter: null });
|
||||
const { groupDialogMemberFilterKey } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
expect(groupDialogMemberFilterKey.value).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('groupDialogMemberFilterGroups', () => {
|
||||
test('includes filter options and role groups', () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
ref: {
|
||||
roles: [
|
||||
{ id: 'role_1', name: 'Admin', defaultRole: false },
|
||||
{ id: 'role_2', name: 'Member', defaultRole: true }
|
||||
],
|
||||
memberCount: 10
|
||||
}
|
||||
});
|
||||
const { groupDialogMemberFilterGroups } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
const groups = groupDialogMemberFilterGroups.value;
|
||||
|
||||
expect(groups.length).toBeGreaterThanOrEqual(1);
|
||||
// should have a filters group
|
||||
const filtersGroup = groups.find((g) => g.key === 'filters');
|
||||
expect(filtersGroup).toBeDefined();
|
||||
expect(filtersGroup.items.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('excludes default roles from role items', () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
ref: {
|
||||
roles: [
|
||||
{ id: 'role_1', name: 'Admin', defaultRole: false },
|
||||
{ id: 'role_2', name: 'Default', defaultRole: true }
|
||||
],
|
||||
memberCount: 10
|
||||
}
|
||||
});
|
||||
const { groupDialogMemberFilterGroups } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
const rolesGroup = groupDialogMemberFilterGroups.value.find(
|
||||
(g) => g.key === 'roles'
|
||||
);
|
||||
|
||||
if (rolesGroup) {
|
||||
expect(rolesGroup.items).toHaveLength(1);
|
||||
expect(rolesGroup.items[0].label).toBe('Admin');
|
||||
}
|
||||
});
|
||||
|
||||
test('omits roles group when no non-default roles exist', () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
ref: {
|
||||
roles: [
|
||||
{ id: 'role_1', name: 'Default', defaultRole: true }
|
||||
],
|
||||
memberCount: 10
|
||||
}
|
||||
});
|
||||
const { groupDialogMemberFilterGroups } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
const rolesGroup = groupDialogMemberFilterGroups.value.find(
|
||||
(g) => g.key === 'roles'
|
||||
);
|
||||
expect(rolesGroup).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('groupMembersSearch', () => {
|
||||
test('clears results when search is less than 3 characters', () => {
|
||||
const groupDialog = createGroupDialog({ memberSearch: 'ab' });
|
||||
const { groupMembersSearch, isGroupMembersLoading } =
|
||||
useGroupMembers(groupDialog, createDeps());
|
||||
|
||||
groupMembersSearch();
|
||||
|
||||
expect(groupDialog.value.memberSearchResults).toEqual([]);
|
||||
expect(isGroupMembersLoading.value).toBe(false);
|
||||
});
|
||||
|
||||
test('calls API when search is 3 or more characters', async () => {
|
||||
const groupDialog = createGroupDialog({ memberSearch: 'abc' });
|
||||
groupRequest.getGroupMembersSearch.mockResolvedValue({
|
||||
json: { results: [{ userId: 'usr_1' }] },
|
||||
params: { groupId: 'grp_1' }
|
||||
});
|
||||
|
||||
const deps = createDeps();
|
||||
const { groupMembersSearch } = useGroupMembers(groupDialog, deps);
|
||||
groupMembersSearch();
|
||||
|
||||
// wait for the debounced call
|
||||
await vi.waitFor(() => {
|
||||
expect(groupRequest.getGroupMembersSearch).toHaveBeenCalledWith(
|
||||
{
|
||||
groupId: 'grp_1',
|
||||
query: 'abc',
|
||||
n: 100,
|
||||
offset: 0
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadMoreGroupMembers', () => {
|
||||
test('does not load when already done', async () => {
|
||||
const groupDialog = createGroupDialog();
|
||||
const { loadMoreGroupMembers, isGroupMembersDone } =
|
||||
useGroupMembers(groupDialog, createDeps());
|
||||
isGroupMembersDone.value = true;
|
||||
|
||||
await loadMoreGroupMembers();
|
||||
|
||||
expect(groupRequest.getCachedGroupMembers).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('does not load when already loading', async () => {
|
||||
const groupDialog = createGroupDialog();
|
||||
const { loadMoreGroupMembers, isGroupMembersLoading } =
|
||||
useGroupMembers(groupDialog, createDeps());
|
||||
isGroupMembersLoading.value = true;
|
||||
|
||||
await loadMoreGroupMembers();
|
||||
|
||||
expect(groupRequest.getCachedGroupMembers).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('marks done when fewer than n results returned', async () => {
|
||||
const groupDialog = createGroupDialog();
|
||||
groupRequest.getCachedGroupMembers.mockResolvedValue({
|
||||
json: [{ userId: 'usr_1' }],
|
||||
params: { groupId: 'grp_1', n: 100, offset: 0 }
|
||||
});
|
||||
|
||||
const {
|
||||
loadMoreGroupMembers,
|
||||
isGroupMembersDone,
|
||||
loadMoreGroupMembersParams
|
||||
} = useGroupMembers(groupDialog, createDeps());
|
||||
|
||||
loadMoreGroupMembersParams.value = {
|
||||
n: 100,
|
||||
offset: 0,
|
||||
groupId: 'grp_1',
|
||||
sort: 'joinedAt:desc'
|
||||
};
|
||||
|
||||
await loadMoreGroupMembers();
|
||||
|
||||
expect(isGroupMembersDone.value).toBe(true);
|
||||
});
|
||||
|
||||
test('appends members to groupDialog.members', async () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
members: [{ userId: 'existing' }]
|
||||
});
|
||||
groupRequest.getCachedGroupMembers.mockResolvedValue({
|
||||
json: [{ userId: 'usr_new' }],
|
||||
params: { groupId: 'grp_1', n: 100, offset: 0 }
|
||||
});
|
||||
|
||||
const { loadMoreGroupMembers, loadMoreGroupMembersParams } =
|
||||
useGroupMembers(groupDialog, createDeps());
|
||||
|
||||
loadMoreGroupMembersParams.value = {
|
||||
n: 100,
|
||||
offset: 0,
|
||||
groupId: 'grp_1',
|
||||
sort: 'joinedAt:desc'
|
||||
};
|
||||
|
||||
await loadMoreGroupMembers();
|
||||
|
||||
expect(groupDialog.value.members).toHaveLength(2);
|
||||
});
|
||||
|
||||
test('removes duplicate current user from first position', async () => {
|
||||
const deps = createDeps();
|
||||
const groupDialog = createGroupDialog({
|
||||
members: [{ userId: 'usr_me' }]
|
||||
});
|
||||
groupRequest.getCachedGroupMembers.mockResolvedValue({
|
||||
json: [{ userId: 'usr_me' }, { userId: 'usr_2' }],
|
||||
params: { groupId: 'grp_1', n: 100, offset: 0 }
|
||||
});
|
||||
|
||||
const { loadMoreGroupMembers, loadMoreGroupMembersParams } =
|
||||
useGroupMembers(groupDialog, deps);
|
||||
|
||||
loadMoreGroupMembersParams.value = {
|
||||
n: 100,
|
||||
offset: 0,
|
||||
groupId: 'grp_1',
|
||||
sort: 'joinedAt:desc'
|
||||
};
|
||||
|
||||
await loadMoreGroupMembers();
|
||||
|
||||
// duplicate at position 0 should be removed
|
||||
const userIds = groupDialog.value.members.map((m) => m.userId);
|
||||
expect(userIds).toEqual(['usr_me', 'usr_2']);
|
||||
});
|
||||
|
||||
test('marks done on error', async () => {
|
||||
const groupDialog = createGroupDialog();
|
||||
groupRequest.getCachedGroupMembers.mockRejectedValue(
|
||||
new Error('fail')
|
||||
);
|
||||
|
||||
const {
|
||||
loadMoreGroupMembers,
|
||||
isGroupMembersDone,
|
||||
loadMoreGroupMembersParams
|
||||
} = useGroupMembers(groupDialog, createDeps());
|
||||
|
||||
loadMoreGroupMembersParams.value = {
|
||||
n: 100,
|
||||
offset: 0,
|
||||
groupId: 'grp_1',
|
||||
sort: 'joinedAt:desc'
|
||||
};
|
||||
|
||||
await expect(loadMoreGroupMembers()).rejects.toThrow('fail');
|
||||
expect(isGroupMembersDone.value).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setGroupMemberSortOrder', () => {
|
||||
test('does not reload when sort order unchanged', async () => {
|
||||
const groupDialog = createGroupDialog({
|
||||
memberSortOrder: { value: 'joinedAt:desc' }
|
||||
});
|
||||
const { setGroupMemberSortOrder } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
|
||||
await setGroupMemberSortOrder({ value: 'joinedAt:desc' });
|
||||
|
||||
expect(groupRequest.getCachedGroupMembers).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setGroupMemberFilter', () => {
|
||||
test('does not reload when filter unchanged', async () => {
|
||||
const { markRaw } = require('vue');
|
||||
const filter = markRaw(groupDialogFilterOptions.everyone);
|
||||
const groupDialog = createGroupDialog();
|
||||
// Use markRaw to prevent Vue from wrapping the filter in a Proxy
|
||||
groupDialog.value.memberFilter = filter;
|
||||
const { setGroupMemberFilter } = useGroupMembers(
|
||||
groupDialog,
|
||||
createDeps()
|
||||
);
|
||||
|
||||
await setGroupMemberFilter(filter);
|
||||
|
||||
expect(groupRequest.getCachedGroupMembers).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user