mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-30 12:13:48 +02:00
add ut
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useActivityDataFilter } from '../useActivityDataFilter';
|
||||
|
||||
function setup({
|
||||
detailData = [],
|
||||
isDetailVisible = true,
|
||||
isSoloInstanceVisible = true,
|
||||
isNoFriendInstanceVisible = true
|
||||
} = {}) {
|
||||
return useActivityDataFilter(
|
||||
ref(detailData),
|
||||
ref(isDetailVisible),
|
||||
ref(isSoloInstanceVisible),
|
||||
ref(isNoFriendInstanceVisible)
|
||||
);
|
||||
}
|
||||
|
||||
describe('useActivityDataFilter', () => {
|
||||
it('returns empty array when isDetailVisible is false', () => {
|
||||
const { filteredActivityDetailData } = setup({
|
||||
detailData: [[{ isFriend: true }]],
|
||||
isDetailVisible: false
|
||||
});
|
||||
expect(filteredActivityDetailData.value).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns all data when all filters are enabled', () => {
|
||||
const data = [
|
||||
[{ isFriend: true }],
|
||||
[{ isFriend: false }],
|
||||
[{ isFriend: true }, { isFriend: false }]
|
||||
];
|
||||
const { filteredActivityDetailData } = setup({ detailData: data });
|
||||
expect(filteredActivityDetailData.value).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('filters solo instances when isSoloInstanceVisible is false', () => {
|
||||
const data = [
|
||||
[{ isFriend: true }], // solo — filtered
|
||||
[{ isFriend: true }, { isFriend: false }] // not solo — kept
|
||||
];
|
||||
const { filteredActivityDetailData } = setup({
|
||||
detailData: data,
|
||||
isSoloInstanceVisible: false
|
||||
});
|
||||
expect(filteredActivityDetailData.value).toHaveLength(1);
|
||||
expect(filteredActivityDetailData.value[0]).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('filters no-friend instances when isNoFriendInstanceVisible is false', () => {
|
||||
const data = [
|
||||
[{ isFriend: false }, { isFriend: false }], // no friends — filtered
|
||||
[{ isFriend: true }, { isFriend: false }] // has friend — kept
|
||||
];
|
||||
const { filteredActivityDetailData } = setup({
|
||||
detailData: data,
|
||||
isNoFriendInstanceVisible: false
|
||||
});
|
||||
expect(filteredActivityDetailData.value).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('keeps solo instances even when isNoFriendInstanceVisible is false', () => {
|
||||
const data = [
|
||||
[{ isFriend: false }] // solo — special case, kept
|
||||
];
|
||||
const { filteredActivityDetailData } = setup({
|
||||
detailData: data,
|
||||
isNoFriendInstanceVisible: false
|
||||
});
|
||||
expect(filteredActivityDetailData.value).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('combines solo and no-friend filters', () => {
|
||||
const data = [
|
||||
[{ isFriend: false }], // solo — filtered by solo filter
|
||||
[{ isFriend: false }, { isFriend: false }], // no friends — filtered
|
||||
[{ isFriend: true }, { isFriend: false }] // kept
|
||||
];
|
||||
const { filteredActivityDetailData } = setup({
|
||||
detailData: data,
|
||||
isSoloInstanceVisible: false,
|
||||
isNoFriendInstanceVisible: false
|
||||
});
|
||||
expect(filteredActivityDetailData.value).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('returns empty array for empty input data', () => {
|
||||
const { filteredActivityDetailData } = setup({ detailData: [] });
|
||||
expect(filteredActivityDetailData.value).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useActivityStats } from '../useActivityStats';
|
||||
|
||||
describe('useActivityStats', () => {
|
||||
it('sums all time values from activityData', () => {
|
||||
const data = ref([{ time: 1000 }, { time: 2000 }, { time: 3000 }]);
|
||||
const { totalOnlineTime } = useActivityStats(data);
|
||||
expect(totalOnlineTime.value).toBe(6000);
|
||||
});
|
||||
|
||||
it('returns 0 for empty array', () => {
|
||||
const data = ref([]);
|
||||
const { totalOnlineTime } = useActivityStats(data);
|
||||
expect(totalOnlineTime.value).toBe(0);
|
||||
});
|
||||
|
||||
it('returns undefined when activityData is null', () => {
|
||||
const data = ref(null);
|
||||
const { totalOnlineTime } = useActivityStats(data);
|
||||
expect(totalOnlineTime.value).toBeUndefined();
|
||||
});
|
||||
|
||||
it('handles single item', () => {
|
||||
const data = ref([{ time: 42 }]);
|
||||
const { totalOnlineTime } = useActivityStats(data);
|
||||
expect(totalOnlineTime.value).toBe(42);
|
||||
});
|
||||
|
||||
it('reacts to changes in activityData', () => {
|
||||
const data = ref([{ time: 100 }]);
|
||||
const { totalOnlineTime } = useActivityStats(data);
|
||||
|
||||
expect(totalOnlineTime.value).toBe(100);
|
||||
|
||||
data.value = [{ time: 200 }, { time: 300 }];
|
||||
expect(totalOnlineTime.value).toBe(500);
|
||||
});
|
||||
});
|
||||
148
src/views/Charts/composables/__tests__/useChartHelpers.test.js
Normal file
148
src/views/Charts/composables/__tests__/useChartHelpers.test.js
Normal file
@@ -0,0 +1,148 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
findMatchingDetailData,
|
||||
formatWorldName,
|
||||
generateYAxisLabel,
|
||||
isDetailDataFiltered
|
||||
} from '../useChartHelpers';
|
||||
|
||||
describe('isDetailDataFiltered', () => {
|
||||
it('returns false when both filters are enabled', () => {
|
||||
const detailData = [{ isFriend: true }, { isFriend: false }];
|
||||
expect(isDetailDataFiltered(detailData, true, true)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when detailData is null/undefined', () => {
|
||||
expect(isDetailDataFiltered(null, false, false)).toBe(false);
|
||||
expect(isDetailDataFiltered(undefined, true, false)).toBe(false);
|
||||
});
|
||||
|
||||
it('filters solo instance when isSoloInstanceVisible is false and only 1 entry', () => {
|
||||
const detailData = [{ isFriend: false }];
|
||||
expect(isDetailDataFiltered(detailData, false, true)).toBe(true);
|
||||
});
|
||||
|
||||
it('does not filter solo when isSoloInstanceVisible is true', () => {
|
||||
const detailData = [{ isFriend: false }];
|
||||
expect(isDetailDataFiltered(detailData, true, true)).toBe(false);
|
||||
});
|
||||
|
||||
it('filters no-friend instance when isNoFriendInstanceVisible is false', () => {
|
||||
const detailData = [{ isFriend: false }, { isFriend: false }];
|
||||
expect(isDetailDataFiltered(detailData, true, false)).toBe(true);
|
||||
});
|
||||
|
||||
it('does not filter when at least one friend exists', () => {
|
||||
const detailData = [{ isFriend: true }, { isFriend: false }];
|
||||
expect(isDetailDataFiltered(detailData, true, false)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findMatchingDetailData', () => {
|
||||
const currentUser = { id: 'user1' };
|
||||
|
||||
it('returns null when activityItem is null', () => {
|
||||
expect(findMatchingDetailData(null, [], currentUser)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when currentUser is null', () => {
|
||||
expect(
|
||||
findMatchingDetailData({ location: 'loc1' }, [], null)
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('finds matching detail data by location and joinTime', () => {
|
||||
const joinTime = { isSame: (other) => other === 100 };
|
||||
const activityItem = { location: 'wrld_abc', joinTime: 100 };
|
||||
const detailData = [
|
||||
[
|
||||
{ location: 'wrld_abc', user_id: 'user1', joinTime },
|
||||
{
|
||||
location: 'wrld_abc',
|
||||
user_id: 'user2',
|
||||
joinTime: { isSame: () => false }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
location: 'wrld_xyz',
|
||||
user_id: 'user1',
|
||||
joinTime: { isSame: () => false }
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
const result = findMatchingDetailData(
|
||||
activityItem,
|
||||
detailData,
|
||||
currentUser
|
||||
);
|
||||
expect(result).toBe(detailData[0]);
|
||||
});
|
||||
|
||||
it('returns undefined when no match is found', () => {
|
||||
const activityItem = { location: 'wrld_abc', joinTime: 100 };
|
||||
const detailData = [
|
||||
[
|
||||
{
|
||||
location: 'wrld_xyz',
|
||||
user_id: 'user1',
|
||||
joinTime: { isSame: () => false }
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
const result = findMatchingDetailData(
|
||||
activityItem,
|
||||
detailData,
|
||||
currentUser
|
||||
);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateYAxisLabel', () => {
|
||||
it('returns filtered label format for filtered data', () => {
|
||||
expect(generateYAxisLabel('TestWorld', true)).toBe(
|
||||
'{filtered|TestWorld}'
|
||||
);
|
||||
});
|
||||
|
||||
it('returns normal label format for non-filtered data', () => {
|
||||
expect(generateYAxisLabel('TestWorld', false)).toBe(
|
||||
'{normal|TestWorld}'
|
||||
);
|
||||
});
|
||||
|
||||
it('truncates long world names', () => {
|
||||
const longName = 'A'.repeat(30);
|
||||
const result = generateYAxisLabel(longName, false);
|
||||
expect(result).toBe(`{normal|${'A'.repeat(20)}...}`);
|
||||
});
|
||||
|
||||
it('respects custom maxLength', () => {
|
||||
const result = generateYAxisLabel('Hello World!', false, 5);
|
||||
expect(result).toBe('{normal|Hello...}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatWorldName', () => {
|
||||
it('returns name as-is when within maxLength', () => {
|
||||
expect(formatWorldName('Short')).toBe('Short');
|
||||
});
|
||||
|
||||
it('truncates and adds ellipsis when name exceeds maxLength', () => {
|
||||
const longName = 'A'.repeat(25);
|
||||
expect(formatWorldName(longName)).toBe(`${'A'.repeat(20)}...`);
|
||||
});
|
||||
|
||||
it('respects custom maxLength', () => {
|
||||
expect(formatWorldName('Hello World', 5)).toBe('Hello...');
|
||||
});
|
||||
|
||||
it('does not truncate at exact maxLength boundary', () => {
|
||||
const exactName = 'A'.repeat(20);
|
||||
expect(formatWorldName(exactName)).toBe(exactName);
|
||||
});
|
||||
});
|
||||
172
src/views/Charts/composables/__tests__/useDateNavigation.test.js
Normal file
172
src/views/Charts/composables/__tests__/useDateNavigation.test.js
Normal file
@@ -0,0 +1,172 @@
|
||||
import { beforeAll, describe, expect, it, vi } from 'vitest';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
|
||||
import { useDateNavigation } from '../useDateNavigation';
|
||||
|
||||
beforeAll(() => {
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(isSameOrAfter);
|
||||
dayjs.tz.setDefault('UTC');
|
||||
});
|
||||
|
||||
function makeDates(...strings) {
|
||||
return ref(new Set(strings));
|
||||
}
|
||||
|
||||
function setup(dateStrings, reloadData = vi.fn()) {
|
||||
const allDates = makeDates(...dateStrings);
|
||||
const result = useDateNavigation(allDates, reloadData);
|
||||
return { ...result, reloadData };
|
||||
}
|
||||
|
||||
describe('useDateNavigation', () => {
|
||||
describe('changeSelectedDateFromBtn', () => {
|
||||
it('navigates to previous date', () => {
|
||||
const dates = ['2025-01-03', '2025-01-02', '2025-01-01'];
|
||||
const { selectedDate, changeSelectedDateFromBtn, reloadData } =
|
||||
setup(dates);
|
||||
|
||||
// Start at the latest date
|
||||
selectedDate.value = dayjs('2025-01-03').toDate();
|
||||
|
||||
changeSelectedDateFromBtn(false); // go prev
|
||||
expect(dayjs(selectedDate.value).format('YYYY-MM-DD')).toBe(
|
||||
'2025-01-02'
|
||||
);
|
||||
expect(reloadData).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('navigates to next date', () => {
|
||||
const dates = ['2025-01-03', '2025-01-02', '2025-01-01'];
|
||||
const { selectedDate, changeSelectedDateFromBtn, reloadData } =
|
||||
setup(dates);
|
||||
|
||||
selectedDate.value = dayjs('2025-01-02').toDate();
|
||||
|
||||
changeSelectedDateFromBtn(true); // go next
|
||||
expect(dayjs(selectedDate.value).format('YYYY-MM-DD')).toBe(
|
||||
'2025-01-03'
|
||||
);
|
||||
expect(reloadData).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does nothing when allDateOfActivity is empty', () => {
|
||||
const { selectedDate, changeSelectedDateFromBtn, reloadData } =
|
||||
setup([]);
|
||||
const original = selectedDate.value;
|
||||
|
||||
changeSelectedDateFromBtn(false);
|
||||
expect(selectedDate.value).toBe(original);
|
||||
expect(reloadData).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('finds nearest previous date when current date is not in array', () => {
|
||||
const dates = ['2025-01-05', '2025-01-03', '2025-01-01'];
|
||||
const { selectedDate, changeSelectedDateFromBtn, reloadData } =
|
||||
setup(dates);
|
||||
|
||||
// Set to a date not in the array
|
||||
selectedDate.value = dayjs('2025-01-04').toDate();
|
||||
|
||||
changeSelectedDateFromBtn(false);
|
||||
// Should find 2025-01-03 as the closest previous date
|
||||
expect(dayjs(selectedDate.value).format('YYYY-MM-DD')).toBe(
|
||||
'2025-01-03'
|
||||
);
|
||||
expect(reloadData).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('falls back to last date when going prev at boundary', () => {
|
||||
const dates = ['2025-01-03', '2025-01-01'];
|
||||
const { selectedDate, changeSelectedDateFromBtn } = setup(dates);
|
||||
|
||||
selectedDate.value = dayjs('2025-01-01').toDate();
|
||||
changeSelectedDateFromBtn(false);
|
||||
// Should stay at or fallback to the last date
|
||||
expect(dayjs(selectedDate.value).format('YYYY-MM-DD')).toBe(
|
||||
'2025-01-01'
|
||||
);
|
||||
});
|
||||
|
||||
it('falls back to first date when going next at boundary', () => {
|
||||
const dates = ['2025-01-03', '2025-01-01'];
|
||||
const { selectedDate, changeSelectedDateFromBtn } = setup(dates);
|
||||
|
||||
selectedDate.value = dayjs('2025-01-03').toDate();
|
||||
changeSelectedDateFromBtn(true);
|
||||
// Already at the latest, should fallback to first
|
||||
expect(dayjs(selectedDate.value).format('YYYY-MM-DD')).toBe(
|
||||
'2025-01-03'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNextDayBtnDisabled', () => {
|
||||
it('is true when selected date is the latest', () => {
|
||||
const dates = ['2025-01-03', '2025-01-02', '2025-01-01'];
|
||||
const { selectedDate, isNextDayBtnDisabled } = setup(dates);
|
||||
|
||||
selectedDate.value = dayjs('2025-01-03').toDate();
|
||||
expect(isNextDayBtnDisabled.value).toBe(true);
|
||||
});
|
||||
|
||||
it('is false when selected date is not the latest', () => {
|
||||
const dates = ['2025-01-03', '2025-01-02', '2025-01-01'];
|
||||
const { selectedDate, isNextDayBtnDisabled } = setup(dates);
|
||||
|
||||
selectedDate.value = dayjs('2025-01-02').toDate();
|
||||
expect(isNextDayBtnDisabled.value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPrevDayBtnDisabled', () => {
|
||||
it('is true when selected date is the earliest', () => {
|
||||
const dates = ['2025-01-03', '2025-01-02', '2025-01-01'];
|
||||
const { selectedDate, isPrevDayBtnDisabled } = setup(dates);
|
||||
|
||||
selectedDate.value = dayjs('2025-01-01').toDate();
|
||||
expect(isPrevDayBtnDisabled.value).toBe(true);
|
||||
});
|
||||
|
||||
it('is false when selected date is not the earliest', () => {
|
||||
const dates = ['2025-01-03', '2025-01-02', '2025-01-01'];
|
||||
const { selectedDate, isPrevDayBtnDisabled } = setup(dates);
|
||||
|
||||
selectedDate.value = dayjs('2025-01-02').toDate();
|
||||
expect(isPrevDayBtnDisabled.value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDatePickerDisabledDate', () => {
|
||||
it('disables future dates', () => {
|
||||
const dates = ['2025-01-03', '2025-01-02', '2025-01-01'];
|
||||
const { getDatePickerDisabledDate } = setup(dates);
|
||||
|
||||
const futureDate = new Date(Date.now() + 86400000);
|
||||
expect(getDatePickerDisabledDate(futureDate)).toBe(true);
|
||||
});
|
||||
|
||||
it('disables dates not in the activity set', () => {
|
||||
const dates = ['2025-01-03', '2025-01-01'];
|
||||
const { getDatePickerDisabledDate } = setup(dates);
|
||||
|
||||
// 2025-01-02 is not in the set
|
||||
const missingDate = dayjs('2025-01-02').toDate();
|
||||
expect(getDatePickerDisabledDate(missingDate)).toBe(true);
|
||||
});
|
||||
|
||||
it('enables dates that are in the activity set', () => {
|
||||
const dates = ['2025-01-03', '2025-01-02', '2025-01-01'];
|
||||
const { getDatePickerDisabledDate } = setup(dates);
|
||||
|
||||
const validDate = dayjs('2025-01-02').toDate();
|
||||
expect(getDatePickerDisabledDate(validDate)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user