view data fixes
This commit is contained in:
@@ -1,24 +1,25 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { AdminUserViewModel, DashboardStatsViewModel, UserListViewModel } from './AdminUserViewModel';
|
||||
import type { UserDto, DashboardStats } from '@/lib/api/admin/AdminApiClient';
|
||||
import type { AdminUserViewData } from '@/lib/view-data/AdminUserViewData';
|
||||
import type { DashboardStatsViewData } from '@/lib/view-data/DashboardStatsViewData';
|
||||
|
||||
describe('AdminUserViewModel', () => {
|
||||
const createBaseDto = (): UserDto => ({
|
||||
const createBaseViewData = (): AdminUserViewData => ({
|
||||
id: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: new Date('2024-01-01T00:00:00Z'),
|
||||
updatedAt: new Date('2024-01-02T00:00:00Z'),
|
||||
lastLoginAt: new Date('2024-01-15T10:30:00Z'),
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
updatedAt: '2024-01-02T00:00:00Z',
|
||||
lastLoginAt: '2024-01-15T10:30:00Z',
|
||||
primaryDriverId: 'driver-456',
|
||||
});
|
||||
|
||||
it('maps core fields from DTO', () => {
|
||||
const dto = createBaseDto();
|
||||
const vm = new AdminUserViewModel(dto);
|
||||
it('maps core fields from ViewData', () => {
|
||||
const viewData = createBaseViewData();
|
||||
const vm = new AdminUserViewModel(viewData);
|
||||
|
||||
expect(vm.id).toBe('user-123');
|
||||
expect(vm.email).toBe('test@example.com');
|
||||
@@ -30,8 +31,8 @@ describe('AdminUserViewModel', () => {
|
||||
});
|
||||
|
||||
it('converts dates to Date objects', () => {
|
||||
const dto = createBaseDto();
|
||||
const vm = new AdminUserViewModel(dto);
|
||||
const viewData = createBaseViewData();
|
||||
const vm = new AdminUserViewModel(viewData);
|
||||
|
||||
expect(vm.createdAt).toBeInstanceOf(Date);
|
||||
expect(vm.updatedAt).toBeInstanceOf(Date);
|
||||
@@ -40,19 +41,19 @@ describe('AdminUserViewModel', () => {
|
||||
});
|
||||
|
||||
it('handles missing lastLoginAt', () => {
|
||||
const dto = createBaseDto();
|
||||
delete dto.lastLoginAt;
|
||||
const vm = new AdminUserViewModel(dto);
|
||||
const viewData = createBaseViewData();
|
||||
delete viewData.lastLoginAt;
|
||||
const vm = new AdminUserViewModel(viewData);
|
||||
|
||||
expect(vm.lastLoginAt).toBeUndefined();
|
||||
expect(vm.lastLoginFormatted).toBe('Never');
|
||||
});
|
||||
|
||||
it('formats role badges correctly', () => {
|
||||
const owner = new AdminUserViewModel({ ...createBaseDto(), roles: ['owner'] });
|
||||
const admin = new AdminUserViewModel({ ...createBaseDto(), roles: ['admin'] });
|
||||
const user = new AdminUserViewModel({ ...createBaseDto(), roles: ['user'] });
|
||||
const custom = new AdminUserViewModel({ ...createBaseDto(), roles: ['custom-role'] });
|
||||
const owner = new AdminUserViewModel({ ...createBaseViewData(), roles: ['owner'] });
|
||||
const admin = new AdminUserViewModel({ ...createBaseViewData(), roles: ['admin'] });
|
||||
const user = new AdminUserViewModel({ ...createBaseViewData(), roles: ['user'] });
|
||||
const custom = new AdminUserViewModel({ ...createBaseViewData(), roles: ['custom-role'] });
|
||||
|
||||
expect(owner.roleBadges).toEqual(['Owner']);
|
||||
expect(admin.roleBadges).toEqual(['Admin']);
|
||||
@@ -61,51 +62,36 @@ describe('AdminUserViewModel', () => {
|
||||
});
|
||||
|
||||
it('derives status badge correctly', () => {
|
||||
const active = new AdminUserViewModel({ ...createBaseDto(), status: 'active' });
|
||||
const suspended = new AdminUserViewModel({ ...createBaseDto(), status: 'suspended' });
|
||||
const deleted = new AdminUserViewModel({ ...createBaseDto(), status: 'deleted' });
|
||||
const active = new AdminUserViewModel({ ...createBaseViewData(), status: 'active' });
|
||||
const suspended = new AdminUserViewModel({ ...createBaseViewData(), status: 'suspended' });
|
||||
const deleted = new AdminUserViewModel({ ...createBaseViewData(), status: 'deleted' });
|
||||
|
||||
expect(active.statusBadge).toEqual({ label: 'Active', variant: 'performance-green' });
|
||||
expect(suspended.statusBadge).toEqual({ label: 'Suspended', variant: 'yellow-500' });
|
||||
expect(deleted.statusBadge).toEqual({ label: 'Deleted', variant: 'racing-red' });
|
||||
expect(active.statusBadgeLabel).toBe('Active');
|
||||
expect(active.statusBadgeVariant).toBe('performance-green');
|
||||
expect(suspended.statusBadgeLabel).toBe('Suspended');
|
||||
expect(suspended.statusBadgeVariant).toBe('yellow-500');
|
||||
expect(deleted.statusBadgeLabel).toBe('Deleted');
|
||||
expect(deleted.statusBadgeVariant).toBe('racing-red');
|
||||
});
|
||||
|
||||
it('formats dates for display', () => {
|
||||
const dto = createBaseDto();
|
||||
const vm = new AdminUserViewModel(dto);
|
||||
const viewData = createBaseViewData();
|
||||
const vm = new AdminUserViewModel(viewData);
|
||||
|
||||
expect(vm.lastLoginFormatted).toBe('1/15/2024');
|
||||
expect(vm.createdAtFormatted).toBe('1/1/2024');
|
||||
});
|
||||
|
||||
it('derives action permissions correctly', () => {
|
||||
const active = new AdminUserViewModel({ ...createBaseDto(), status: 'active' });
|
||||
const suspended = new AdminUserViewModel({ ...createBaseDto(), status: 'suspended' });
|
||||
const deleted = new AdminUserViewModel({ ...createBaseDto(), status: 'deleted' });
|
||||
|
||||
expect(active.canSuspend).toBe(true);
|
||||
expect(active.canActivate).toBe(false);
|
||||
expect(active.canDelete).toBe(true);
|
||||
|
||||
expect(suspended.canSuspend).toBe(false);
|
||||
expect(suspended.canActivate).toBe(true);
|
||||
expect(suspended.canDelete).toBe(true);
|
||||
|
||||
expect(deleted.canSuspend).toBe(false);
|
||||
expect(deleted.canActivate).toBe(false);
|
||||
expect(deleted.canDelete).toBe(false);
|
||||
expect(vm.lastLoginFormatted).toBe('Jan 15, 2024');
|
||||
expect(vm.createdAtFormatted).toBe('Jan 1, 2024');
|
||||
});
|
||||
|
||||
it('handles multiple roles', () => {
|
||||
const dto = { ...createBaseDto(), roles: ['owner', 'admin'] };
|
||||
const vm = new AdminUserViewModel(dto);
|
||||
const viewData = { ...createBaseViewData(), roles: ['owner', 'admin'] };
|
||||
const vm = new AdminUserViewModel(viewData);
|
||||
|
||||
expect(vm.roleBadges).toEqual(['Owner', 'Admin']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DashboardStatsViewModel', () => {
|
||||
const createBaseData = (): DashboardStats => ({
|
||||
const createBaseData = (): DashboardStatsViewData => ({
|
||||
totalUsers: 100,
|
||||
activeUsers: 70,
|
||||
suspendedUsers: 10,
|
||||
@@ -165,21 +151,24 @@ describe('DashboardStatsViewModel', () => {
|
||||
totalUsers: 100,
|
||||
recentLogins: 10, // 10% engagement
|
||||
});
|
||||
expect(lowEngagement.activityLevel).toBe('low');
|
||||
expect(lowEngagement.activityLevelLabel).toBe('Low');
|
||||
expect(lowEngagement.activityLevelValue).toBe('low');
|
||||
|
||||
const mediumEngagement = new DashboardStatsViewModel({
|
||||
...createBaseData(),
|
||||
totalUsers: 100,
|
||||
recentLogins: 35, // 35% engagement
|
||||
});
|
||||
expect(mediumEngagement.activityLevel).toBe('medium');
|
||||
expect(mediumEngagement.activityLevelLabel).toBe('Medium');
|
||||
expect(mediumEngagement.activityLevelValue).toBe('medium');
|
||||
|
||||
const highEngagement = new DashboardStatsViewModel({
|
||||
...createBaseData(),
|
||||
totalUsers: 100,
|
||||
recentLogins: 60, // 60% engagement
|
||||
});
|
||||
expect(highEngagement.activityLevel).toBe('high');
|
||||
expect(highEngagement.activityLevelLabel).toBe('High');
|
||||
expect(highEngagement.activityLevelValue).toBe('high');
|
||||
});
|
||||
|
||||
it('handles zero users safely', () => {
|
||||
@@ -194,7 +183,8 @@ describe('DashboardStatsViewModel', () => {
|
||||
expect(vm.activeRate).toBe(0);
|
||||
expect(vm.activeRateFormatted).toBe('0%');
|
||||
expect(vm.adminRatio).toBe('1:1');
|
||||
expect(vm.activityLevel).toBe('low');
|
||||
expect(vm.activityLevelLabel).toBe('Low');
|
||||
expect(vm.activityLevelValue).toBe('low');
|
||||
});
|
||||
|
||||
it('preserves arrays from input', () => {
|
||||
@@ -208,21 +198,21 @@ describe('DashboardStatsViewModel', () => {
|
||||
});
|
||||
|
||||
describe('UserListViewModel', () => {
|
||||
const createDto = (overrides: Partial<UserDto> = {}): UserDto => ({
|
||||
const createViewData = (overrides: Partial<AdminUserViewData> = {}): AdminUserViewData => ({
|
||||
id: 'user-1',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: new Date('2024-01-01'),
|
||||
updatedAt: new Date('2024-01-02'),
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
updatedAt: '2024-01-02T00:00:00Z',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
it('wraps user DTOs in AdminUserViewModel instances', () => {
|
||||
it('wraps user ViewData in AdminUserViewModel instances', () => {
|
||||
const data = {
|
||||
users: [createDto({ id: 'user-1' }), createDto({ id: 'user-2' })],
|
||||
users: [createViewData({ id: 'user-1' }), createViewData({ id: 'user-2' })],
|
||||
total: 2,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
@@ -239,7 +229,7 @@ describe('UserListViewModel', () => {
|
||||
|
||||
it('exposes pagination metadata', () => {
|
||||
const data = {
|
||||
users: [createDto()],
|
||||
users: [createViewData()],
|
||||
total: 50,
|
||||
page: 2,
|
||||
limit: 10,
|
||||
@@ -256,7 +246,7 @@ describe('UserListViewModel', () => {
|
||||
|
||||
it('derives hasUsers correctly', () => {
|
||||
const withUsers = new UserListViewModel({
|
||||
users: [createDto()],
|
||||
users: [createViewData()],
|
||||
total: 1,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
@@ -277,7 +267,7 @@ describe('UserListViewModel', () => {
|
||||
|
||||
it('derives showPagination correctly', () => {
|
||||
const withPagination = new UserListViewModel({
|
||||
users: [createDto()],
|
||||
users: [createViewData()],
|
||||
total: 20,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
@@ -285,7 +275,7 @@ describe('UserListViewModel', () => {
|
||||
});
|
||||
|
||||
const withoutPagination = new UserListViewModel({
|
||||
users: [createDto()],
|
||||
users: [createViewData()],
|
||||
total: 5,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
@@ -298,7 +288,7 @@ describe('UserListViewModel', () => {
|
||||
|
||||
it('calculates start and end indices correctly', () => {
|
||||
const vm = new UserListViewModel({
|
||||
users: [createDto(), createDto(), createDto()],
|
||||
users: [createViewData(), createViewData(), createViewData()],
|
||||
total: 50,
|
||||
page: 2,
|
||||
limit: 10,
|
||||
|
||||
Reference in New Issue
Block a user