Files
Genarrative/src/components/rpg-entry/rpgEntryProfileTaskViewModel.test.ts
T

128 lines
3.4 KiB
TypeScript

import { expect, test } from 'vitest';
import type {
ProfileTaskCenterResponse,
ProfileTaskItem,
} from '../../../packages/shared/src/contracts/runtime';
import {
buildProfileTaskCardSummary,
buildProfileTaskProgressLabel,
getProfileTaskClaimButtonLabel,
getProfileTaskStatusLabel,
selectProfileTaskCardTask,
selectProfileTaskCenterTasks,
} from './rpgEntryProfileTaskViewModel';
function buildTask(
overrides: Partial<ProfileTaskItem> = {},
): ProfileTaskItem {
return {
taskId: 'task-1',
title: '游玩一次',
description: '完成一次游戏',
eventKey: 'work_play_start',
cycle: 'daily',
threshold: 1,
progressCount: 0,
rewardPoints: 10,
status: 'incomplete',
dayKey: 20260603,
claimedAt: null,
updatedAt: '2026-06-03T00:00:00.000Z',
...overrides,
};
}
function buildCenter(
tasks: ProfileTaskItem[],
): ProfileTaskCenterResponse {
return {
dayKey: 20260603,
walletBalance: 12,
tasks,
updatedAt: '2026-06-03T00:00:00.000Z',
};
}
test('profile task ViewModel selects one actionable task by status priority and original order', () => {
const firstIncomplete = buildTask({
taskId: 'incomplete-1',
status: 'incomplete',
});
const secondIncomplete = buildTask({
taskId: 'incomplete-2',
status: 'incomplete',
});
const claimable = buildTask({
taskId: 'claimable-1',
status: 'claimable',
});
expect(
selectProfileTaskCenterTasks([
firstIncomplete,
secondIncomplete,
claimable,
]),
).toEqual([claimable]);
expect(selectProfileTaskCenterTasks([firstIncomplete, secondIncomplete])).toEqual(
[firstIncomplete],
);
});
test('profile task ViewModel falls back from card task to claimed and enabled tasks', () => {
const claimed = buildTask({ taskId: 'claimed-1', status: 'claimed' });
const disabled = buildTask({ taskId: 'disabled-1', status: 'disabled' });
const incomplete = buildTask({
taskId: 'incomplete-1',
status: 'incomplete',
});
expect(selectProfileTaskCardTask([disabled, claimed])).toBe(claimed);
expect(selectProfileTaskCardTask([disabled, incomplete])).toBe(incomplete);
expect(selectProfileTaskCardTask([disabled])).toBeNull();
});
test('profile task ViewModel builds card summary with reward fallback and clamped progress', () => {
expect(buildProfileTaskCardSummary(null)).toEqual({
actionLabel: '去完成',
progressCount: 0,
progressPercent: 0,
rewardPoints: 10,
threshold: 1,
});
expect(
buildProfileTaskCardSummary(
buildCenter([
buildTask({
progressCount: 5,
rewardPoints: 25,
status: 'claimable',
threshold: 3,
}),
]),
),
).toEqual({
actionLabel: '领取',
progressCount: 3,
progressPercent: 100,
rewardPoints: 25,
threshold: 3,
});
});
test('profile task ViewModel exposes task labels for the modal', () => {
const task = buildTask({ progressCount: -1, threshold: 0 });
expect(getProfileTaskStatusLabel('claimable')).toBe('可领取');
expect(buildProfileTaskProgressLabel(task)).toBe('0/1');
expect(getProfileTaskClaimButtonLabel(task, true)).toBe('领取中');
expect(getProfileTaskClaimButtonLabel(buildTask({ status: 'claimed' }), false)).toBe(
'已领取',
);
expect(
getProfileTaskClaimButtonLabel(buildTask({ status: 'claimable' }), false),
).toBe('领取');
});