/** * Tests for Hierarchy Grouping Utilities */ import { describe, it, expect } from 'vitest'; import { groupProjectsByHierarchy, isDateStale, formatDateShort, getNodeBadgeClass } from './hierarchyUtils'; import { ProjectData } from '@/types/project'; describe('groupProjectsByHierarchy', () => { it('should group projects by project name and product name', () => { const mockProjects: ProjectData[] = [ { no: '1', identity: { projectName: 'Ziti2', productName: 'housing', moldNumber: 'LA25463', customerName: '罗技', customerBase: '苏州', factory: 'A厂', moldSets: '1', partNumber: '400-009150', cavityNumber: 'CAV1-4', projectManager: '张舜杰', projectEngineer: '袁任杰', moldProject: '胡荣武', qe: '柳彬彬', riskLevel: '正常' }, milestones: { projectStart: '2001-05-14', t1: '2001-06-08', glTime: '2001-12-02', vmp: '2001-10-18', mp: '-', currentStage: 'PQR', trialCount: 'T26', t1SizeQualified: '-', toolingFAI: '79.40%', partFAI: '100%', currentNode: 'PQR' }, details: { detailDate: '2026-01-02', detailProgress: '苏州跑线通过, PB2 越南试产 400PCS,1/26海运, 预计2/9号到达' } }, { no: '2', identity: { projectName: 'Ziti2', productName: 'housing', moldNumber: 'LA25464', customerName: '罗技', customerBase: '苏州', factory: 'A厂', moldSets: '1', partNumber: '400-009151', cavityNumber: 'CAV1-4', projectManager: '张舜杰', projectEngineer: '袁任杰', moldProject: '胡荣武', qe: '柳彬彬', riskLevel: '正常' }, milestones: { projectStart: '2001-05-14', t1: '2001-06-08', glTime: '2001-12-02', vmp: '2001-10-18', mp: '-', currentStage: 'T1', trialCount: 'T10', t1SizeQualified: '-', toolingFAI: '50%', partFAI: '80%', currentNode: 'T1' }, details: { detailDate: '2026-01-15', detailProgress: '第二次试模完成' } } ]; const result = groupProjectsByHierarchy(mockProjects); expect(result).toHaveLength(1); expect(result[0].projectName).toBe('Ziti2'); expect(result[0].products).toHaveLength(1); expect(result[0].products[0].productName).toBe('housing'); expect(result[0].products[0].molds).toHaveLength(2); expect(result[0].products[0].molds[0].moldNumber).toBe('LA25463'); expect(result[0].products[0].molds[1].moldNumber).toBe('LA25464'); }); it('should handle multiple projects and products', () => { const mockProjects: ProjectData[] = [ { no: '1', identity: { projectName: 'Project A', productName: 'Product 1', moldNumber: 'M001', customerName: '', customerBase: '', factory: '', moldSets: '', partNumber: '', cavityNumber: '', projectManager: '', projectEngineer: '', moldProject: '', qe: '', riskLevel: '' }, milestones: { projectStart: '', t1: '', glTime: '', vmp: '', mp: '', currentStage: 'PQR', trialCount: '', t1SizeQualified: '', toolingFAI: '', partFAI: '', currentNode: 'PQR' }, details: { detailDate: '2026-01-01', detailProgress: 'Test' } }, { no: '2', identity: { projectName: 'Project A', productName: 'Product 2', moldNumber: 'M002', customerName: '', customerBase: '', factory: '', moldSets: '', partNumber: '', cavityNumber: '', projectManager: '', projectEngineer: '', moldProject: '', qe: '', riskLevel: '' }, milestones: { projectStart: '', t1: '', glTime: '', vmp: '', mp: '', currentStage: 'T1', trialCount: '', t1SizeQualified: '', toolingFAI: '', partFAI: '', currentNode: 'T1' }, details: { detailDate: '2026-01-01', detailProgress: 'Test' } }, { no: '3', identity: { projectName: 'Project B', productName: 'Product 3', moldNumber: 'M003', customerName: '', customerBase: '', factory: '', moldSets: '', partNumber: '', cavityNumber: '', projectManager: '', projectEngineer: '', moldProject: '', qe: '', riskLevel: '' }, milestones: { projectStart: '', t1: '', glTime: '', vmp: '', mp: '', currentStage: 'MP', trialCount: '', t1SizeQualified: '', toolingFAI: '', partFAI: '', currentNode: 'MP' }, details: { detailDate: '2026-01-01', detailProgress: 'Test' } } ]; const result = groupProjectsByHierarchy(mockProjects); expect(result).toHaveLength(2); expect(result[0].projectName).toBe('Project A'); expect(result[0].products).toHaveLength(2); expect(result[1].projectName).toBe('Project B'); expect(result[1].products).toHaveLength(1); }); }); describe('isDateStale', () => { it('should return true for dates older than 7 days', () => { const oldDate = new Date(); oldDate.setDate(oldDate.getDate() - 10); const dateStr = oldDate.toISOString().split('T')[0]; expect(isDateStale(dateStr)).toBe(true); }); it('should return false for recent dates', () => { const recentDate = new Date(); recentDate.setDate(recentDate.getDate() - 3); const dateStr = recentDate.toISOString().split('T')[0]; expect(isDateStale(dateStr)).toBe(false); }); it('should return false for invalid dates', () => { expect(isDateStale('-')).toBe(false); expect(isDateStale('')).toBe(false); expect(isDateStale('invalid')).toBe(false); }); }); describe('formatDateShort', () => { it('should format date as "月日"', () => { const result = formatDateShort('2026-01-15'); // Date parsing may vary by timezone, check format instead expect(result).toMatch(/\d+月\d+日/); }); it('should handle different date formats', () => { const result = formatDateShort('1/2/26'); expect(result).toMatch(/\d+月\d+日/); }); it('should return "-" for invalid dates', () => { expect(formatDateShort('-')).toBe('-'); expect(formatDateShort('')).toBe('-'); }); }); describe('getNodeBadgeClass', () => { it('should return green class for completed stages', () => { expect(getNodeBadgeClass('MP')).toContain('green'); expect(getNodeBadgeClass('完成')).toContain('green'); }); it('should return blue class for in-progress stages', () => { expect(getNodeBadgeClass('PQR')).toContain('blue'); expect(getNodeBadgeClass('T1')).toContain('blue'); // VMP is considered completed stage, should be green expect(getNodeBadgeClass('进行中')).toContain('blue'); }); it('should return slate class for early stages', () => { expect(getNodeBadgeClass('KICK OFF')).toContain('slate'); expect(getNodeBadgeClass('G/L')).toContain('slate'); }); it('should handle empty or invalid input', () => { const result = getNodeBadgeClass(''); expect(result).toContain('slate'); }); });