feat: 增强情绪博物馆项目功能 - 新增用户评论和帖子功能,优化前端架构和WebSocket通信 - 更新文档和部署配置
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* 格式化工具函数测试
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import {
|
||||
formatDate,
|
||||
formatDateTime,
|
||||
formatTime,
|
||||
formatRelativeTime,
|
||||
formatFileSize,
|
||||
formatNumber,
|
||||
formatCurrency,
|
||||
maskPhone,
|
||||
maskEmail,
|
||||
truncateText
|
||||
} from '@/utils/format'
|
||||
|
||||
describe('format utils', () => {
|
||||
beforeEach(() => {
|
||||
// 重置时间相关的模拟
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(new Date('2024-01-15 12:00:00'))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
describe('formatDate', () => {
|
||||
it('should format timestamp to date string', () => {
|
||||
const timestamp = new Date('2024-01-15').getTime()
|
||||
expect(formatDate(timestamp)).toBe('2024-01-15')
|
||||
})
|
||||
|
||||
it('should format Date object to date string', () => {
|
||||
const date = new Date('2024-01-15')
|
||||
expect(formatDate(date)).toBe('2024-01-15')
|
||||
})
|
||||
|
||||
it('should use custom format', () => {
|
||||
const timestamp = new Date('2024-01-15').getTime()
|
||||
expect(formatDate(timestamp, 'MM/DD/YYYY')).toBe('01/15/2024')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatDateTime', () => {
|
||||
it('should format timestamp to datetime string', () => {
|
||||
const timestamp = new Date('2024-01-15 12:30:45').getTime()
|
||||
expect(formatDateTime(timestamp)).toBe('2024-01-15 12:30:45')
|
||||
})
|
||||
|
||||
it('should use custom format', () => {
|
||||
const timestamp = new Date('2024-01-15 12:30:45').getTime()
|
||||
expect(formatDateTime(timestamp, 'YYYY年MM月DD日 HH:mm')).toBe('2024年01月15日 12:30')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatTime', () => {
|
||||
it('should format timestamp to time string', () => {
|
||||
const timestamp = new Date('2024-01-15 12:30:45').getTime()
|
||||
expect(formatTime(timestamp)).toBe('12:30:45')
|
||||
})
|
||||
|
||||
it('should use custom format', () => {
|
||||
const timestamp = new Date('2024-01-15 12:30:45').getTime()
|
||||
expect(formatTime(timestamp, 'HH:mm')).toBe('12:30')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatRelativeTime', () => {
|
||||
it('should return "刚刚" for very recent time', () => {
|
||||
const timestamp = Date.now() - 1000 // 1秒前
|
||||
expect(formatRelativeTime(timestamp)).toBe('刚刚')
|
||||
})
|
||||
|
||||
it('should return minutes ago', () => {
|
||||
const timestamp = Date.now() - 5 * 60 * 1000 // 5分钟前
|
||||
expect(formatRelativeTime(timestamp)).toBe('5分钟前')
|
||||
})
|
||||
|
||||
it('should return hours ago', () => {
|
||||
const timestamp = Date.now() - 2 * 60 * 60 * 1000 // 2小时前
|
||||
expect(formatRelativeTime(timestamp)).toBe('2小时前')
|
||||
})
|
||||
|
||||
it('should return days ago', () => {
|
||||
const timestamp = Date.now() - 3 * 24 * 60 * 60 * 1000 // 3天前
|
||||
expect(formatRelativeTime(timestamp)).toBe('3天前')
|
||||
})
|
||||
|
||||
it('should return formatted date for old time', () => {
|
||||
const timestamp = Date.now() - 10 * 24 * 60 * 60 * 1000 // 10天前
|
||||
expect(formatRelativeTime(timestamp)).toMatch(/\d{4}-\d{2}-\d{2}/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatFileSize', () => {
|
||||
it('should format bytes', () => {
|
||||
expect(formatFileSize(512)).toBe('512 B')
|
||||
})
|
||||
|
||||
it('should format KB', () => {
|
||||
expect(formatFileSize(1024)).toBe('1.0 KB')
|
||||
expect(formatFileSize(1536)).toBe('1.5 KB')
|
||||
})
|
||||
|
||||
it('should format MB', () => {
|
||||
expect(formatFileSize(1024 * 1024)).toBe('1.0 MB')
|
||||
expect(formatFileSize(1024 * 1024 * 2.5)).toBe('2.5 MB')
|
||||
})
|
||||
|
||||
it('should format GB', () => {
|
||||
expect(formatFileSize(1024 * 1024 * 1024)).toBe('1.0 GB')
|
||||
})
|
||||
|
||||
it('should handle zero size', () => {
|
||||
expect(formatFileSize(0)).toBe('0 B')
|
||||
})
|
||||
|
||||
it('should handle negative size', () => {
|
||||
expect(formatFileSize(-1024)).toBe('0 B')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatNumber', () => {
|
||||
it('should format number with default options', () => {
|
||||
expect(formatNumber(1234.567)).toBe('1,234.567')
|
||||
})
|
||||
|
||||
it('should format number with custom decimal places', () => {
|
||||
expect(formatNumber(1234.567, { decimals: 2 })).toBe('1,234.57')
|
||||
})
|
||||
|
||||
it('should format number without separator', () => {
|
||||
expect(formatNumber(1234.567, { separator: false })).toBe('1234.567')
|
||||
})
|
||||
|
||||
it('should handle zero', () => {
|
||||
expect(formatNumber(0)).toBe('0')
|
||||
})
|
||||
|
||||
it('should handle negative numbers', () => {
|
||||
expect(formatNumber(-1234.567)).toBe('-1,234.567')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatCurrency', () => {
|
||||
it('should format currency with default options', () => {
|
||||
expect(formatCurrency(1234.56)).toBe('¥1,234.56')
|
||||
})
|
||||
|
||||
it('should format currency with custom symbol', () => {
|
||||
expect(formatCurrency(1234.56, { symbol: '$' })).toBe('$1,234.56')
|
||||
})
|
||||
|
||||
it('should format currency with custom decimal places', () => {
|
||||
expect(formatCurrency(1234.567, { decimals: 3 })).toBe('¥1,234.567')
|
||||
})
|
||||
})
|
||||
|
||||
describe('maskPhone', () => {
|
||||
it('should mask phone number', () => {
|
||||
expect(maskPhone('13812345678')).toBe('138****5678')
|
||||
})
|
||||
|
||||
it('should handle short phone number', () => {
|
||||
expect(maskPhone('12345')).toBe('12345')
|
||||
})
|
||||
|
||||
it('should handle empty phone number', () => {
|
||||
expect(maskPhone('')).toBe('')
|
||||
})
|
||||
|
||||
it('should handle null/undefined', () => {
|
||||
expect(maskPhone(null)).toBe('')
|
||||
expect(maskPhone(undefined)).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('maskEmail', () => {
|
||||
it('should mask email address', () => {
|
||||
expect(maskEmail('test@example.com')).toBe('t***@example.com')
|
||||
})
|
||||
|
||||
it('should handle short email', () => {
|
||||
expect(maskEmail('a@b.c')).toBe('a***@b.c')
|
||||
})
|
||||
|
||||
it('should handle invalid email', () => {
|
||||
expect(maskEmail('invalid-email')).toBe('invalid-email')
|
||||
})
|
||||
|
||||
it('should handle empty email', () => {
|
||||
expect(maskEmail('')).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('truncateText', () => {
|
||||
it('should truncate long text', () => {
|
||||
const text = 'This is a very long text that should be truncated'
|
||||
expect(truncateText(text, 20)).toBe('This is a very long...')
|
||||
})
|
||||
|
||||
it('should not truncate short text', () => {
|
||||
const text = 'Short text'
|
||||
expect(truncateText(text, 20)).toBe('Short text')
|
||||
})
|
||||
|
||||
it('should use custom suffix', () => {
|
||||
const text = 'This is a very long text'
|
||||
expect(truncateText(text, 10, '---')).toBe('This is a---')
|
||||
})
|
||||
|
||||
it('should handle empty text', () => {
|
||||
expect(truncateText('', 10)).toBe('')
|
||||
})
|
||||
|
||||
it('should handle zero length', () => {
|
||||
expect(truncateText('Hello', 0)).toBe('...')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* 验证工具函数测试
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
validateEmail,
|
||||
validatePhone,
|
||||
validatePassword,
|
||||
validateUsername,
|
||||
validateUrl,
|
||||
validateIdCard,
|
||||
validateRequired,
|
||||
validateLength,
|
||||
validateNumber,
|
||||
validateInteger,
|
||||
validatePositive,
|
||||
validateRange
|
||||
} from '@/utils/validation'
|
||||
|
||||
describe('validation utils', () => {
|
||||
describe('validateEmail', () => {
|
||||
it('should validate correct email addresses', () => {
|
||||
expect(validateEmail('test@example.com')).toBe(true)
|
||||
expect(validateEmail('user.name@domain.co.uk')).toBe(true)
|
||||
expect(validateEmail('user+tag@example.org')).toBe(true)
|
||||
expect(validateEmail('123@456.com')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject invalid email addresses', () => {
|
||||
expect(validateEmail('invalid-email')).toBe(false)
|
||||
expect(validateEmail('test@')).toBe(false)
|
||||
expect(validateEmail('@example.com')).toBe(false)
|
||||
expect(validateEmail('test..test@example.com')).toBe(false)
|
||||
expect(validateEmail('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validatePhone', () => {
|
||||
it('should validate correct phone numbers', () => {
|
||||
expect(validatePhone('13812345678')).toBe(true)
|
||||
expect(validatePhone('15987654321')).toBe(true)
|
||||
expect(validatePhone('18612345678')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject invalid phone numbers', () => {
|
||||
expect(validatePhone('12345678901')).toBe(false) // 不是1开头
|
||||
expect(validatePhone('1381234567')).toBe(false) // 长度不够
|
||||
expect(validatePhone('138123456789')).toBe(false) // 长度过长
|
||||
expect(validatePhone('13a12345678')).toBe(false) // 包含字母
|
||||
expect(validatePhone('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validatePassword', () => {
|
||||
it('should validate correct passwords', () => {
|
||||
expect(validatePassword('abc123')).toBe(true)
|
||||
expect(validatePassword('Password1')).toBe(true)
|
||||
expect(validatePassword('test123456')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject invalid passwords', () => {
|
||||
expect(validatePassword('12345')).toBe(false) // 长度不够
|
||||
expect(validatePassword('abcdef')).toBe(false) // 只有字母
|
||||
expect(validatePassword('123456')).toBe(false) // 只有数字
|
||||
expect(validatePassword('')).toBe(false)
|
||||
expect(validatePassword('a'.repeat(21))).toBe(false) // 长度过长
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateUsername', () => {
|
||||
it('should validate correct usernames', () => {
|
||||
expect(validateUsername('user123')).toBe(true)
|
||||
expect(validateUsername('test_user')).toBe(true)
|
||||
expect(validateUsername('用户名')).toBe(true)
|
||||
expect(validateUsername('user_123')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject invalid usernames', () => {
|
||||
expect(validateUsername('ab')).toBe(false) // 长度不够
|
||||
expect(validateUsername('user-name')).toBe(false) // 包含连字符
|
||||
expect(validateUsername('user@name')).toBe(false) // 包含特殊字符
|
||||
expect(validateUsername('')).toBe(false)
|
||||
expect(validateUsername('a'.repeat(21))).toBe(false) // 长度过长
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateUrl', () => {
|
||||
it('should validate correct URLs', () => {
|
||||
expect(validateUrl('https://example.com')).toBe(true)
|
||||
expect(validateUrl('http://test.org')).toBe(true)
|
||||
expect(validateUrl('https://sub.domain.com/path?query=1')).toBe(true)
|
||||
expect(validateUrl('ftp://files.example.com')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject invalid URLs', () => {
|
||||
expect(validateUrl('not-a-url')).toBe(false)
|
||||
expect(validateUrl('example.com')).toBe(false) // 缺少协议
|
||||
expect(validateUrl('http://')).toBe(false)
|
||||
expect(validateUrl('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateIdCard', () => {
|
||||
it('should validate correct ID card numbers', () => {
|
||||
expect(validateIdCard('110101199003077777')).toBe(true)
|
||||
expect(validateIdCard('11010119900307777X')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject invalid ID card numbers', () => {
|
||||
expect(validateIdCard('12345678901234567')).toBe(false) // 长度不够
|
||||
expect(validateIdCard('1234567890123456789')).toBe(false) // 长度过长
|
||||
expect(validateIdCard('11010119900307777Y')).toBe(false) // 最后一位不是X
|
||||
expect(validateIdCard('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateRequired', () => {
|
||||
it('should validate required values', () => {
|
||||
expect(validateRequired('test')).toBe(true)
|
||||
expect(validateRequired(123)).toBe(true)
|
||||
expect(validateRequired(0)).toBe(true)
|
||||
expect(validateRequired(false)).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject empty values', () => {
|
||||
expect(validateRequired('')).toBe(false)
|
||||
expect(validateRequired(' ')).toBe(false) // 只有空格
|
||||
expect(validateRequired(null)).toBe(false)
|
||||
expect(validateRequired(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateLength', () => {
|
||||
it('should validate correct length', () => {
|
||||
expect(validateLength('test', 3, 5)).toBe(true)
|
||||
expect(validateLength('hello', 5, 10)).toBe(true)
|
||||
expect(validateLength('ab', 1, 3)).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject incorrect length', () => {
|
||||
expect(validateLength('ab', 3, 5)).toBe(false) // 太短
|
||||
expect(validateLength('toolong', 3, 5)).toBe(false) // 太长
|
||||
expect(validateLength('', 1, 5)).toBe(false) // 空字符串
|
||||
})
|
||||
|
||||
it('should handle edge cases', () => {
|
||||
expect(validateLength('test', 4, 4)).toBe(true) // 正好等于边界
|
||||
expect(validateLength('test', 0, 10)).toBe(true) // 最小长度为0
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateNumber', () => {
|
||||
it('should validate numbers', () => {
|
||||
expect(validateNumber(123)).toBe(true)
|
||||
expect(validateNumber(0)).toBe(true)
|
||||
expect(validateNumber(-456)).toBe(true)
|
||||
expect(validateNumber(3.14)).toBe(true)
|
||||
expect(validateNumber('123')).toBe(true)
|
||||
expect(validateNumber('3.14')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject non-numbers', () => {
|
||||
expect(validateNumber('abc')).toBe(false)
|
||||
expect(validateNumber('12abc')).toBe(false)
|
||||
expect(validateNumber('')).toBe(false)
|
||||
expect(validateNumber(null)).toBe(false)
|
||||
expect(validateNumber(undefined)).toBe(false)
|
||||
expect(validateNumber(NaN)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateInteger', () => {
|
||||
it('should validate integers', () => {
|
||||
expect(validateInteger(123)).toBe(true)
|
||||
expect(validateInteger(0)).toBe(true)
|
||||
expect(validateInteger(-456)).toBe(true)
|
||||
expect(validateInteger('123')).toBe(true)
|
||||
expect(validateInteger('-456')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject non-integers', () => {
|
||||
expect(validateInteger(3.14)).toBe(false)
|
||||
expect(validateInteger('3.14')).toBe(false)
|
||||
expect(validateInteger('abc')).toBe(false)
|
||||
expect(validateInteger('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validatePositive', () => {
|
||||
it('should validate positive numbers', () => {
|
||||
expect(validatePositive(123)).toBe(true)
|
||||
expect(validatePositive(0.1)).toBe(true)
|
||||
expect(validatePositive('123')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject non-positive numbers', () => {
|
||||
expect(validatePositive(0)).toBe(false)
|
||||
expect(validatePositive(-123)).toBe(false)
|
||||
expect(validatePositive('-123')).toBe(false)
|
||||
expect(validatePositive('abc')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateRange', () => {
|
||||
it('should validate numbers in range', () => {
|
||||
expect(validateRange(5, 1, 10)).toBe(true)
|
||||
expect(validateRange(1, 1, 10)).toBe(true) // 边界值
|
||||
expect(validateRange(10, 1, 10)).toBe(true) // 边界值
|
||||
expect(validateRange('5', 1, 10)).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject numbers out of range', () => {
|
||||
expect(validateRange(0, 1, 10)).toBe(false)
|
||||
expect(validateRange(11, 1, 10)).toBe(false)
|
||||
expect(validateRange(-5, 1, 10)).toBe(false)
|
||||
expect(validateRange('abc', 1, 10)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user