【调整】增加部署雨云

This commit is contained in:
cai
2026-01-13 17:47:39 +08:00
parent 4e49ca075a
commit 367c1a1096
1094 changed files with 179074 additions and 45 deletions

View File

@@ -0,0 +1,382 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import * as browserUtils from '../src/browser'
describe('浏览器工具函数测试', () => {
beforeEach(() => {
vi.resetModules() // 清理所有模拟和存储
localStorage.clear()
sessionStorage.clear()
document.cookie = ''
})
describe('环境检测', () => {
describe('isHttps', () => {
it('应当正确判断 HTTPS 协议', () => {
const locationSpy = vi.spyOn(window, 'location', 'get')
locationSpy.mockReturnValue({ protocol: 'https:' } as Location)
expect(browserUtils.isHttps()).toBe(true)
locationSpy.mockRestore()
})
it('应当正确判断非 HTTPS 协议', () => {
const locationSpy = vi.spyOn(window, 'location', 'get')
locationSpy.mockReturnValue({ protocol: 'http:' } as Location)
expect(browserUtils.isHttps()).toBe(false)
locationSpy.mockRestore()
})
})
describe('isDev', () => {
it('应当正确判断开发环境', () => {
const originalEnv = process.env.NODE_ENV
process.env.NODE_ENV = 'development'
expect(browserUtils.isDev()).toBe(true)
process.env.NODE_ENV = originalEnv
})
it('应当正确判断非开发环境', () => {
const originalEnv = process.env.NODE_ENV
process.env.NODE_ENV = 'production'
expect(browserUtils.isDev()).toBe(false)
process.env.NODE_ENV = originalEnv
})
})
})
describe('浏览器信息获取', () => {
describe('getBrowserOSInfo', () => {
it.each([
[
'Chrome',
'Windows',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
],
['Firefox', 'macOS', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:91.0) Gecko/20100101 Firefox/91.0'],
[
'Safari',
'iOS',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1',
],
[
'Edge',
'Windows',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59',
],
['Unknown', 'Linux', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)'],
])('应当正确识别 %s 浏览器和 %s 系统', (browser, os, userAgent) => {
Object.defineProperty(navigator, 'userAgent', { value: userAgent, configurable: true })
const info = browserUtils.getBrowserOSInfo()
expect(info.browser).toBe(browser)
expect(info.os).toBe(os)
})
})
describe('getScreenInfo', () => {
it('应当返回正确的屏幕信息', () => {
Object.defineProperty(window, 'screen', {
value: { width: 1920, height: 1080 },
configurable: true,
})
Object.defineProperty(window, 'devicePixelRatio', { value: 2, configurable: true })
const screenInfo = browserUtils.getScreenInfo()
expect(screenInfo.resolution).toBe('1920x1080')
expect(screenInfo.scale).toBe(2)
})
})
})
describe('URL参数操作', () => {
describe('getUrlParam', () => {
it('应当正确获取URL参数', () => {
const locationSpy = vi.spyOn(window, 'location', 'get')
locationSpy.mockReturnValue({ search: '?name=test&age=25&empty=&special=%20%26' } as Location)
expect(browserUtils.getUrlParam('name')).toBe('test')
expect(browserUtils.getUrlParam('age')).toBe('25')
expect(browserUtils.getUrlParam('empty')).toBe('')
expect(browserUtils.getUrlParam('special')).toBe(' &')
expect(browserUtils.getUrlParam('notexist')).toBeNull()
locationSpy.mockRestore()
})
})
})
describe('存储操作', () => {
describe('Storage API', () => {
const testData = { name: 'test', value: 123, nested: { key: 'value' } }
const testKey = 'testKey'
describe('localStorage', () => {
it('应当正确设置和获取数据', () => {
browserUtils.setLocalItem(testKey, testData)
expect(browserUtils.getLocalItem(testKey)).toEqual(testData)
})
it('应当正确删除数据', () => {
browserUtils.setLocalItem(testKey, testData)
browserUtils.removeLocalItem(testKey)
expect(browserUtils.getLocalItem(testKey)).toBeNull()
})
it('应当正确清空所有数据', () => {
browserUtils.setLocalItem(testKey, testData)
browserUtils.setLocalItem('otherKey', 'value')
browserUtils.clearLocal()
expect(browserUtils.getLocalItem(testKey)).toBeNull()
expect(browserUtils.getLocalItem('otherKey')).toBeNull()
})
})
describe('sessionStorage', () => {
it('应当正确设置和获取数据', () => {
browserUtils.setSessionItem(testKey, testData)
expect(browserUtils.getSessionItem(testKey)).toEqual(testData)
})
it('应当正确删除数据', () => {
browserUtils.setSessionItem(testKey, testData)
browserUtils.removeSessionItem(testKey)
expect(browserUtils.getSessionItem(testKey)).toBeNull()
})
it('应当正确清空所有数据', () => {
browserUtils.setSessionItem(testKey, testData)
browserUtils.setSessionItem('otherKey', 'value')
browserUtils.clearSession()
expect(browserUtils.getSessionItem(testKey)).toBeNull()
expect(browserUtils.getSessionItem('otherKey')).toBeNull()
})
})
})
describe('Cookie API', () => {
const testKey = 'testKey'
const testValue = 'testValue'
beforeEach(() => {
// 清除所有 cookie
document.cookie.split(';').forEach((cookie) => {
const [key] = cookie.split('=')
document.cookie = `${key}=;expires=${new Date(0).toUTCString()};path=/`
})
})
it('应当正确设置和获取 cookie', () => {
browserUtils.setCookie(testKey, testValue)
expect(browserUtils.getCookie(testKey)).toBe(testValue)
})
it('应当正确设置带过期时间的 cookie', () => {
browserUtils.setCookie(testKey, testValue, 1)
expect(browserUtils.getCookie(testKey)).toBe(testValue)
})
it('应当正确处理特殊字符', () => {
const specialValue = 'test value with spaces & special chars'
browserUtils.setCookie(testKey, specialValue)
expect(browserUtils.getCookie(testKey)).toBe(specialValue)
})
it('应当正确删除 cookie', () => {
browserUtils.setCookie(testKey, testValue)
browserUtils.deleteCookie(testKey)
expect(browserUtils.getCookie(testKey)).toBeNull()
})
it('应当正确清空所有 cookie', () => {
browserUtils.setCookie(testKey, testValue)
browserUtils.setCookie('otherKey', 'otherValue')
browserUtils.clearCookie()
expect(browserUtils.getCookie(testKey)).toBeNull()
expect(browserUtils.getCookie('otherKey')).toBeNull()
})
it('应当正确处理 HTTPS 前缀', () => {
const locationSpy = vi.spyOn(window, 'location', 'get')
locationSpy.mockReturnValue({ protocol: 'https:' } as Location)
browserUtils.setCookie(testKey, testValue)
expect(browserUtils.getCookie(testKey)).toBe(testValue)
expect(document.cookie).toContain('https_')
locationSpy.mockRestore()
})
})
})
describe('柯里化函数', () => {
it('应当正确使用柯里化版本的 getUrlParam', () => {
const locationSpy = vi.spyOn(window, 'location', 'get')
locationSpy.mockReturnValue({ search: '?name=test&age=25' } as Location)
expect(browserUtils.getUrlParamCurried('name')).toBe('test')
expect(browserUtils.getUrlParamCurried('age')).toBe('25')
locationSpy.mockRestore()
})
it('应当正确使用柯里化版本的 setCookie', () => {
const setCookieForKey = browserUtils.setCookieCurried('testKey')
setCookieForKey('testValue', 1)
expect(browserUtils.getCookie('testKey')).toBe('testValue')
})
it('应当正确使用柯里化版本的 getCookie', () => {
browserUtils.setCookie('testKey', 'testValue')
expect(browserUtils.getCookieCurried('testKey')).toBe('testValue')
})
it('应当正确使用柯里化版本的 setStorageItem', () => {
const setItemForKey = browserUtils.setStorageItemCurried('testKey')
const testData = { test: 'value' }
setItemForKey(testData, localStorage)
expect(JSON.parse(localStorage.getItem('testKey') || '')).toEqual(testData)
})
it('应当正确使用柯里化版本的 getStorageItem', () => {
const testData = { test: 'value' }
localStorage.setItem('testKey', JSON.stringify(testData))
expect(browserUtils.getStorageItemCurried('testKey')(localStorage)).toEqual(testData)
})
})
describe('IndexedDB', () => {
let dbManager: browserUtils.IndexedDBManager
const testConfig: browserUtils.IndexedDBConfig = {
dbName: 'testDB',
version: 1,
stores: {
users: {
keyPath: 'id',
indexes: [
{ name: 'name', keyPath: 'name' },
{ name: 'email', keyPath: 'email', options: { unique: true } },
],
},
},
}
beforeEach(async () => {
// 确保在创建新的数据库管理器之前删除旧的数据库
await new Promise<void>((resolve) => {
const deleteRequest = indexedDB.deleteDatabase(testConfig.dbName)
deleteRequest.onsuccess = () => resolve()
deleteRequest.onerror = () => resolve() // 即使出错也继续
deleteRequest.onblocked = () => resolve() // 处理阻塞情况
})
// 创建新的数据库管理器实例
dbManager = new browserUtils.IndexedDBManager(testConfig)
// 等待数据库连接和初始化完成
await dbManager.connect()
})
afterEach(async () => {
// 关闭数据库连接
if (dbManager) {
dbManager.close()
}
// 删除测试数据库
await new Promise<void>((resolve) => {
const deleteRequest = indexedDB.deleteDatabase(testConfig.dbName)
deleteRequest.onsuccess = () => resolve()
deleteRequest.onerror = () => resolve() // 即使出错也继续
deleteRequest.onblocked = () => {
// 等待连接关闭后继续
setTimeout(resolve, 100)
}
})
})
it('应当正确连接数据库', async () => {
const db = await dbManager.connect()
expect(db).toBeDefined()
expect(db.name).toBe(testConfig.dbName)
expect(db.version).toBe(testConfig.version)
expect(Array.from(db.objectStoreNames)).toContain('users')
})
it('应当正确添加和获取数据', async () => {
const testUser = { id: 1, name: 'Test User', email: 'test@example.com' }
// 添加数据
await dbManager.add('users', testUser)
// 获取数据
const result = await dbManager.get('users', 1)
expect(result).toEqual(testUser)
})
it('应当正确更新数据', async () => {
const testUser = { id: 1, name: 'Test User', email: 'test@example.com' }
await dbManager.add('users', testUser)
const updatedUser = { ...testUser, name: 'Updated User' }
await dbManager.put('users', updatedUser)
const result = await dbManager.get('users', 1)
expect(result).toEqual(updatedUser)
})
it('应当正确删除数据', async () => {
const testUser = { id: 1, name: 'Test User', email: 'test@example.com' }
await dbManager.add('users', testUser)
await dbManager.delete('users', 1)
const result = await dbManager.get('users', 1)
expect(result).toBeUndefined()
})
it('应当正确通过索引查询数据', async () => {
const testUser = { id: 1, name: 'Test User', email: 'test@example.com' }
await dbManager.add('users', testUser)
const result = await dbManager.getByIndex('users', 'email', 'test@example.com')
expect(result).toEqual(testUser)
})
it('应当正确获取所有数据', async () => {
const users = [
{ id: 1, name: 'User 1', email: 'user1@example.com' },
{ id: 2, name: 'User 2', email: 'user2@example.com' },
]
await dbManager.addBatch('users', users)
const results = await dbManager.getAll('users')
expect(results).toHaveLength(2)
expect(results).toEqual(expect.arrayContaining(users))
})
it('应当正确遍历数据', async () => {
const users = [
{ id: 1, name: 'User 1', email: 'user1@example.com' },
{ id: 2, name: 'User 2', email: 'user2@example.com' },
]
await dbManager.addBatch('users', users)
const results: typeof users = []
await dbManager.forEach<(typeof users)[0]>('users', (item) => {
results.push(item)
})
expect(results).toHaveLength(2)
expect(results).toEqual(expect.arrayContaining(users))
})
it('应当正确清空数据', async () => {
const users = [
{ id: 1, name: 'User 1', email: 'user1@example.com' },
{ id: 2, name: 'User 2', email: 'user2@example.com' },
]
await dbManager.addBatch('users', users)
await dbManager.clear('users')
const results = await dbManager.getAll('users')
expect(results).toHaveLength(0)
})
})
})

View File

@@ -0,0 +1,169 @@
import { describe, it, expect } from 'vitest'
import * as businessUtils from '../src/business'
describe('业务工具函数测试', () => {
describe('正则验证测试', () => {
describe('邮箱验证', () => {
it.each([
['valid@email.com', true],
['invalid.email', false],
['test@test.cn', true],
['@invalid.com', false],
['test@.com', false],
])('应当正确验证邮箱 %s', (email, expected) => {
expect(businessUtils.isEmail(email)).toBe(expected)
})
})
describe('手机号验证', () => {
it.each([
['13812345678', true],
['12345678901', false],
['19912345678', true],
['1381234567', false],
['138123456789', false],
])('应当正确验证手机号 %s', (phone, expected) => {
expect(businessUtils.isPhone(phone)).toBe(expected)
})
})
describe('身份证号验证', () => {
it.each([
['440101199001011234', true],
['44010119900101123X', true],
['440101199001011', false],
['44010119900101123Y', false],
])('应当正确验证身份证号 %s', (idCard, expected) => {
expect(businessUtils.isIdCard(idCard)).toBe(expected)
})
})
describe('URL验证', () => {
it.each([
['https://www.example.com', true],
['http://localhost:3000', true],
['ftp://files.example.com', true],
['invalid-url', false],
])('应当正确验证URL %s', (url, expected) => {
expect(businessUtils.isUrl(url)).toBe(expected)
})
})
describe('IP地址验证', () => {
describe('IPv4验证', () => {
it.each([
['192.168.1.1', true],
['256.1.2.3', false],
['1.2.3.4', true],
['192.168.001.1', false],
])('应当正确验证IPv4地址 %s', (ip, expected) => {
expect(businessUtils.isIpv4(ip)).toBe(expected)
})
})
describe('IPv6验证', () => {
it.each([
['2001:0db8:85a3:0000:0000:8a2e:0370:7334', true],
['fe80::1', true],
['::1', true],
['2001::7334', true],
['invalid-ipv6', false],
])('应当正确验证IPv6地址 %s', (ip, expected) => {
expect(businessUtils.isIpv6(ip)).toBe(expected)
})
})
})
describe('MAC地址验证', () => {
it.each([
['00-B0-D0-63-C2-26', true],
['00-b0-d0-63-c2-26', true],
['00:B0:D0:63:C2:26', false],
['00-B0-D0-63-C2', false],
])('应当正确验证MAC地址 %s', (mac, expected) => {
expect(businessUtils.isMac(mac)).toBe(expected)
})
})
describe('中文验证', () => {
it.each([
['中文', true],
['中文123', false],
['Chinese', false],
['中文!', false],
])('应当正确验证中文 %s', (str, expected) => {
expect(businessUtils.isChinese(str)).toBe(expected)
})
})
})
describe('业务操作测试', () => {
describe('手机号加密', () => {
it('应当正确加密手机号', () => {
expect(businessUtils.encryptPhone('13812345678')).toBe('138****5678')
})
})
describe('身份证号加密', () => {
it('应当正确加密身份证号', () => {
expect(businessUtils.encryptIdCard('440101199001011234')).toBe('440101****1234')
expect(businessUtils.encryptIdCard('44010119900101123X')).toBe('440101****123X')
})
})
describe('版本号比较', () => {
it.each([
['1.0.0', '1.0.1', -1],
['1.0.1', '1.0.0', 1],
['1.0.0', '1.0.0', 0],
['1.0', '1.0.0', 0],
['1.0.0', '1', 0],
['1.1', '1.0.1', 1],
])('比较版本号 %s 和 %s 应当返回 %i', (v1, v2, expected) => {
expect(businessUtils.compareVersion(v1, v2)).toBe(expected)
})
})
describe('字节转换', () => {
it.each([
[0, 2, true, '', '0 B'],
[1024, 0, true, '', '1 KB'],
[1024 * 1024, 2, true, '', '1.00 MB'],
[1024 * 1024 * 1024, 0, true, '', '1 GB'],
[1500, 2, true, 'KB', '1.46 KB'],
[1500, 2, false, 'KB', '1.46'],
])('转换 %i 字节应当返回 %s', (bytes, fixed, isUnit, endUnit, expected) => {
expect(businessUtils.formatBytes(bytes, fixed, isUnit, endUnit)).toBe(expected)
})
})
describe('分页字符串转换', () => {
it.each([
["class='Pcount'>共100条<", 100],
["class='Pcount'>共0条<", 0],
['invalid string', 0],
])('应当正确转换分页字符串 %s', (page, expected) => {
expect(businessUtils.formatPage(page)).toBe(expected)
})
})
})
describe('代理配置测试', () => {
it('应当正确生成params格式的代理配置', () => {
const config = businessUtils.getProxyConfig('test-key', 'params') as {
request_time: number
request_token: string
}
expect(config).toHaveProperty('request_time')
expect(config).toHaveProperty('request_token')
expect(typeof config.request_time).toBe('number')
expect(typeof config.request_token).toBe('string')
})
it('应当正确生成query格式的代理配置', () => {
const config = businessUtils.getProxyConfig('test-key', 'query')
expect(typeof config).toBe('string')
expect(config).toMatch(/request_time=\d+&request_token=[a-f0-9]+/)
})
})
})

View File

@@ -0,0 +1,121 @@
import { describe, it, expect } from 'vitest'
import * as dataUtils from '../src/data'
describe('数据处理工具函数测试', () => {
describe('数据转换', () => {
describe('objectToString', () => {
it('应当正确将对象值转换为字符串', () => {
const input = { a: 1, b: true, c: null }
const expected = { a: '1', b: 'true', c: 'null' }
expect(dataUtils.objectToString(input)).toEqual(expected)
})
})
describe('arrayToObject', () => {
it('应当正确将数组转换为对象', () => {
const input = [
{ id: '1', name: 'test1' },
{ id: '2', name: 'test2' },
]
const expected = {
'1': { id: '1', name: 'test1' },
'2': { id: '2', name: 'test2' },
}
expect(dataUtils.arrayToObject('id', input)).toEqual(expected)
})
})
describe('flattenObject', () => {
it('应当正确扁平化对象', () => {
const input = {
a: 1,
b: {
c: 2,
d: {
e: 3,
},
},
}
const expected = {
a: 1,
'b.c': 2,
'b.d.e': 3,
}
console.log('dataUtils.flattenObject(input)', dataUtils.flattenObject(input))
expect(dataUtils.flattenObject(input)).toEqual(expected)
})
})
})
describe('数据验证', () => {
describe('matchesPattern', () => {
it('应当正确验证正则表达式', () => {
const pattern = /^test\d+$/
expect(dataUtils.matchesPattern(pattern, 'test123')).toBe(true)
expect(dataUtils.matchesPattern(pattern, 'test')).toBe(false)
})
})
describe('hasRequiredKeys', () => {
it('应当正确验证必需的键', () => {
const obj = { a: 1, b: 2, c: 3 }
expect(dataUtils.hasRequiredKeys(obj, ['a', 'b'])).toBe(true)
expect(dataUtils.hasRequiredKeys(obj, ['a', 'd'])).toBe(false)
})
})
describe('isInRange', () => {
it('应当正确验证值是否在范围内', () => {
expect(dataUtils.isInRange(1, 10, 5)).toBe(true)
expect(dataUtils.isInRange(1, 10, 0)).toBe(false)
expect(dataUtils.isInRange(1, 10, 11)).toBe(false)
})
})
})
describe('数据过滤与重组', () => {
describe('filterObject', () => {
it('应当正确过滤对象的属性', () => {
const input = { a: 1, b: null, c: undefined, d: 'test' }
const expected = { a: 1, d: 'test' }
expect(dataUtils.filterObject((value) => value != null, input)).toEqual(expected)
})
})
describe('groupByKey', () => {
it('应当正确按键对数组进行分组', () => {
const input = [
{ type: 'A', value: 1 },
{ type: 'B', value: 2 },
{ type: 'A', value: 3 },
]
const expected = {
A: [
{ type: 'A', value: 1 },
{ type: 'A', value: 3 },
],
B: [{ type: 'B', value: 2 }],
}
expect(dataUtils.groupByKey('type', input)).toEqual(expected)
})
})
describe('pluckDeep', () => {
it('应当正确提取深层属性', () => {
const input = [{ a: { b: { c: 1 } } }, { a: { b: { c: 2 } } }]
expect(dataUtils.pluckDeep(['a', 'b', 'c'], input)).toEqual([1, 2])
})
})
describe('flattenAndUniq', () => {
it('应当正确扁平化和去重数组', () => {
const input = [
[1, 2],
[2, 3],
[3, 4],
]
expect(dataUtils.flattenAndUniq(input)).toEqual([1, 2, 3, 4])
})
})
})
})

View File

@@ -0,0 +1,123 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import * as dateUtils from '../src/date'
describe('日期处理工具函数测试', () => {
describe('formatDate', () => {
it('应当正确格式化日期字符串', () => {
const date = new Date('2024-02-27 14:30:45')
expect(dateUtils.formatDate(date)).toBe('2024-02-27 14:30:45')
expect(dateUtils.formatDate(date, 'YYYY-MM-DD')).toBe('2024-02-27')
expect(dateUtils.formatDate(date, 'HH:mm:ss')).toBe('14:30:45')
})
it('应当正确处理单位数的月日时分秒', () => {
const date = new Date('2024-01-05 09:05:08')
expect(dateUtils.formatDate(date)).toBe('2024-01-05 09:05:08')
})
})
describe('getDaysDiff', () => {
it('应当正确计算两个日期之间的天数差', () => {
const start = new Date('2024-02-27')
const end = new Date('2024-03-01')
expect(dateUtils.getDaysDiff(start, end)).toBe(3)
})
it('应当正确处理同一天的情况', () => {
const date = new Date('2024-02-27')
expect(dateUtils.getDaysDiff(date, date)).toBe(0)
})
})
describe('isDateInRange', () => {
it('应当正确判断日期是否在范围内', () => {
const start = new Date('2024-02-01')
const end = new Date('2024-02-29')
const date = new Date('2024-02-15')
expect(dateUtils.isDateInRange(date, start, end)).toBe(true)
})
it('应当正确处理边界情况', () => {
const start = new Date('2024-02-01')
const end = new Date('2024-02-29')
expect(dateUtils.isDateInRange(start, start, end)).toBe(true)
expect(dateUtils.isDateInRange(end, start, end)).toBe(true)
})
it('应当正确处理范围外的情况', () => {
const start = new Date('2024-02-01')
const end = new Date('2024-02-29')
const before = new Date('2024-01-31')
const after = new Date('2024-03-01')
expect(dateUtils.isDateInRange(before, start, end)).toBe(false)
expect(dateUtils.isDateInRange(after, start, end)).toBe(false)
})
})
describe('getStartOfDay和getEndOfDay', () => {
it('应当正确获取一天的开始时间', () => {
const date = new Date('2024-02-27 14:30:45')
const start = dateUtils.getStartOfDay(date)
expect(start.getHours()).toBe(0)
expect(start.getMinutes()).toBe(0)
expect(start.getSeconds()).toBe(0)
})
it('应当正确获取一天的结束时间', () => {
const date = new Date('2024-02-27 14:30:45')
const end = dateUtils.getEndOfDay(date)
expect(end.getHours()).toBe(23)
expect(end.getMinutes()).toBe(59)
expect(end.getSeconds()).toBe(59)
})
})
describe('addDays', () => {
it('应当正确添加天数', () => {
const date = new Date('2024-02-27')
expect(dateUtils.addDays(1, date).toDateString()).toBe(new Date('2024-02-28').toDateString())
expect(dateUtils.addDays(-1, date).toDateString()).toBe(new Date('2024-02-26').toDateString())
})
})
describe('formatRelativeTime', () => {
beforeEach(() => {
// 固定当前时间为2024-02-27 14:30:00
vi.useFakeTimers()
vi.setSystemTime(new Date('2024-02-27 14:30:00'))
})
afterEach(() => {
vi.useRealTimers()
})
it('应当正确格式化相对时间', () => {
expect(dateUtils.formatRelativeTime(new Date('2024-02-27 14:29:30'))).toBe('刚刚')
expect(dateUtils.formatRelativeTime(new Date('2024-02-27 14:25:00'))).toBe('5分钟前')
expect(dateUtils.formatRelativeTime(new Date('2024-02-27 13:30:00'))).toBe('1小时前')
expect(dateUtils.formatRelativeTime(new Date('2024-02-26 14:30:00'))).toBe('1天前')
expect(dateUtils.formatRelativeTime(new Date('2024-01-27 14:30:00'))).toBe('2024-01-27')
})
})
describe('getDayOfWeek', () => {
it('应当正确获取星期几', () => {
expect(dateUtils.getDayOfWeek(new Date('2024-02-27'))).toBe('星期二')
expect(dateUtils.getDayOfWeek(new Date('2024-02-25'))).toBe('星期日')
})
})
describe('getDaysUntilExpiration', () => {
it('应当正确计算到期天数', () => {
const current = new Date('2024-02-27')
const future = new Date('2024-03-01')
expect(dateUtils.getDaysUntilExpiration(current, future)).toBe('3天')
})
it('应当正确处理已过期情况', () => {
const current = new Date('2024-02-27')
const past = new Date('2024-02-26')
expect(dateUtils.getDaysUntilExpiration(current, past)).toBe('已过期')
})
})
})

View File

@@ -0,0 +1,85 @@
import { describe, it, expect } from 'vitest'
import * as encipherUtils from '../src/encipher'
describe('加密解密工具函数测试', () => {
const publicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArWtsSxxqzT8X9D3yVF12
6WHBd+6WZw1TSoatATB6djpe05xwPKOFrNSbOz/tqm6zOhv47w8roO8p978XmHiv
fOuYZxAoCCJUZBG5BxMgEcO5uwue/ll1Hp5VaxvI52Vnuoh9HLx8LpxB0FPXvAjm
cJ7pvgs8Tnox8o2idWN25D1HTeITME+9wBcs7aubNFoUczFDk5+q33mW+i31C30r
DK9/j0odoy0NYGA5DxQiOWpqK3ljaO+40XWYqbWBfq+9LeTPMKT8UARxiSTXumKL
R5p35l0B1CoqpedhszPFvfHzpIPHSzk+uDAwMdR7EprrGinYzOTiTs/wy/ggOICe
uwIDAQAB
-----END PUBLIC KEY-----`
const privateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCta2xLHGrNPxf0
PfJUXXbpYcF37pZnDVNKhq0BMHp2Ol7TnHA8o4Ws1Js7P+2qbrM6G/jvDyug7yn3
vxeYeK9865hnECgIIlRkEbkHEyARw7m7C57+WXUenlVrG8jnZWe6iH0cvHwunEHQ
U9e8COZwnum+CzxOejHyjaJ1Y3bkPUdN4hMwT73AFyztq5s0WhRzMUOTn6rfeZb6
LfULfSsMr3+PSh2jLQ1gYDkPFCI5amoreWNo77jRdZiptYF+r70t5M8wpPxQBHGJ
JNe6YotHmnfmXQHUKiql52GzM8W98fOkg8dLOT64MDAx1HsSmusaKdjM5OJOz/DL
+CA4gJ67AgMBAAECggEAPS0LC8gfiP375kZACTDbdOLuS++XkQzrV/wAZc4DNVfM
AdxK36lTy69If3NC1P+uLA6YF0UDwAb+iA4aNchFJ804ewsBBDWQDakO24cMphek
mm40DUfjgASc32byzWZBXFUvxYZcTFkFAofBL+z31bzJeigegxSqMAV0zPJki4jZ
pWLiVYiIQ+SSG75mr/c9VJdTV7/kekNpkaXmTaVRSfKqROQpV7niknWnuNYSUGUF
apY7JbEJiIOdB6Tc6aGIXzAAlr3klCF22cTTBbBrP3kDGm2Bmr2Hqrxe5I0Eo05j
9Su+TiH26tBG4/FoMm6l3nT8O2fjntv1eXUuvluLOQKBgQDcB4oCP4f/bIysns8u
dGsdsvoZ/e57QVHcRn5G8A7KYma5uJ96Ll0eWgSLoH3wZ2YuRbqrQX7Y3dvfAd6I
LvB6lOJ6bpDc5bn3wvGXb6qF/9h/m2HIyepE9B7m5omXCZ4tFiThbUX1m/TrhzW8
IWKe8qY25FuIji9thzrFbCdGTQKBgQDJxTbWuXMAv9lojXwu73XPMDlgt5LG0eEK
S4QYxMm7VEHtXQ54q8ExKTETuzcTHADqBBEx7/Zhlv6Bxxbe7ghjHI3Mv+F7T5qq
5zZ8n62c0UWttd0XqbC3jLtiX3wMtM2WnUGdgWA4/YBbWlj7x6cuO8ptL027hR/k
/ta1vz8NJwKBgCBFSsyBnOStewRmVmSt1ngIo/3j7HJPZj40aJjm5IRyYjajCWDW
I/orobcI1u/HeokW2QX9GSmdgH34vDalC8guxfjG9qAvYVMhWGWpjw0QNSSiGXll
g+KRG2cqMMviMTzTnp0hdb1MHmPc9Nie3OQLGq26WGJy2CnsR4ZlEm2RAoGBALof
0Xl8MskDMKNQuLh3Lp7EZnmAfcYn/0bG6IEMrua+T96NE/dewOT/kYUZEzHuiC1X
OSFusUHOztGafM+ClnwO8ANrEa31fcCfbtTBW56oMXWPqPbWEu0OxiB14nG6K1f/
knKf0MphlpEuo50GzIJKp23W0AbmQ8izCA857wjLAoGAYqCMYuQuLWu/0WNyRR7z
ia3CeKh2L8Y/0IhXAqwMFwxZgbHKc9Bw2Q/Vqj45ERhMQntDDzVeUhZZVXVnmWsj
58YF7VfNOok4B0UU4uOGus6XYsAD4mm9gTiaFijPIFY1icd9p8wl7MmVWBlvtqmE
SQK1rJJCb92DHknrKmUUj0o=
-----END RSA PRIVATE KEY-----
`
describe('rsaEncrypt', () => {
it('应当正确加密字符串', () => {
const text = 'Hello World'
const encrypted = encipherUtils.rsaEncrypt(text, publicKey)
expect(encrypted).toBeTruthy()
expect(encrypted).not.toBe(text)
})
it('当公钥无效时应当返回原文', () => {
const text = 'Hello World'
expect(encipherUtils.rsaEncrypt(text, '')).toBe(text)
})
})
describe('rsaDecrypt', () => {
it('应当正确解密字符串', () => {
const text = 'Hello World'
const encrypted = encipherUtils.rsaEncrypt(text, publicKey)
const decrypted = encipherUtils.rsaDecrypt(encrypted, privateKey)
expect(decrypted).toBe(text)
})
it('当私钥无效时应当返回原文', () => {
const text = 'Hello World'
expect(encipherUtils.rsaDecrypt(text, '')).toBe(text)
})
})
describe('加密解密集成测试', () => {
it('应当能够正确完成加密解密循环', () => {
const testCases = ['Hello World', '123456', 'Special @#$% Characters', '中文测试']
testCases.forEach((text) => {
const encrypted = encipherUtils.rsaEncrypt(text, publicKey)
const decrypted = encipherUtils.rsaDecrypt(encrypted, privateKey)
expect(decrypted).toBe(text)
})
})
})
})

View File

@@ -0,0 +1,106 @@
import { describe, it, expect } from 'vitest'
import * as randomUtils from '../src/random'
describe('随机数生成工具函数测试', () => {
describe('randomInt', () => {
it('应当生成指定范围内的随机整数', () => {
const min = 1
const max = 10
for (let i = 0; i < 100; i++) {
const result = randomUtils.randomInt(min, max)
expect(result).toBeGreaterThanOrEqual(min)
expect(result).toBeLessThanOrEqual(max)
expect(Number.isInteger(result)).toBe(true)
}
})
it('应当正确处理最小值等于最大值的情况', () => {
const value = 5
expect(randomUtils.randomInt(value, value)).toBe(value)
})
})
describe('randomChart', () => {
it('应当生成指定长度的随机字符串', () => {
const length = 32
const result = randomUtils.randomChart(length)
expect(result.length).toBe(length)
})
it('应当正确处理不同选项', () => {
// 只包含特殊字符
const specialOnly = randomUtils.randomChart(10, {
isSpecial: true,
isLower: false,
isUpper: false,
isNumber: false,
})
expect(specialOnly).toMatch(/^[!@#$%^&*?]+$/)
// 只包含小写字母
const lowerOnly = randomUtils.randomChart(10, {
isSpecial: false,
isLower: true,
isUpper: false,
isNumber: false,
})
expect(lowerOnly).toMatch(/^[a-z]+$/)
// 只包含大写字母
const upperOnly = randomUtils.randomChart(10, {
isSpecial: false,
isLower: false,
isUpper: true,
isNumber: false,
})
expect(upperOnly).toMatch(/^[A-Z]+$/)
// 只包含数字
const numberOnly = randomUtils.randomChart(10, {
isSpecial: false,
isLower: false,
isUpper: false,
isNumber: true,
})
expect(numberOnly).toMatch(/^[0-9]+$/)
})
})
describe('randomChartWithMinLength', () => {
it('应当生成包含最小长度要求的随机字符串', () => {
const length = 32
const options = {
minUpper: 5,
minLower: 5,
minNumber: 5,
minSpecial: 2,
}
const result = randomUtils.randomChartWithMinLength(length, options)
expect(result.length).toBe(length)
expect(result.match(/[A-Z]/g)?.length).toBeGreaterThanOrEqual(options.minUpper)
expect(result.match(/[a-z]/g)?.length).toBeGreaterThanOrEqual(options.minLower)
expect(result.match(/[0-9]/g)?.length).toBeGreaterThanOrEqual(options.minNumber)
expect(result.match(/[!@#$%^&*?]/g)?.length).toBeGreaterThanOrEqual(options.minSpecial)
})
it('当最小长度要求总和超过指定长度时应当抛出错误', () => {
const length = 10
const options = {
minUpper: 4,
minLower: 4,
minNumber: 4,
minSpecial: 4,
}
expect(() => randomUtils.randomChartWithMinLength(length, options)).toThrow()
})
it('应当生成不同的随机字符串', () => {
const length = 32
const options = { minUpper: 1, minLower: 1, minNumber: 1, minSpecial: 0 }
const result1 = randomUtils.randomChartWithMinLength(length, options)
const result2 = randomUtils.randomChartWithMinLength(length, options)
expect(result1).not.toBe(result2)
})
})
})

View File

@@ -0,0 +1,75 @@
import { describe, it, expect } from 'vitest'
import * as stringUtils from '../src/string'
describe('字符串处理工具函数测试', () => {
describe('urlToObject', () => {
it('应当正确解析URL参数', () => {
const url = 'https://example.com/path?name=test&age=25&type=user'
const result = stringUtils.urlToObject(url)
expect(result).toEqual({
name: 'test',
age: '25',
type: 'user',
})
})
it('应当正确处理空参数', () => {
const url = 'https://example.com/path'
const result = stringUtils.urlToObject(url)
expect(result).toEqual({})
})
it('应当正确处理特殊字符', () => {
const url = 'https://example.com/path?name=test%20name&email=test%40example.com'
const result = stringUtils.urlToObject(url)
expect(result).toEqual({
name: 'test name',
email: 'test@example.com',
})
})
})
describe('htmlEscape', () => {
it('应当正确转义HTML字符', () => {
const html = '<div class="test">Hello & World</div>'
const escaped = stringUtils.htmlEscape(html)
expect(escaped).toBe('&lt;div class=&quot;test&quot;&gt;Hello &amp; World&lt;/div&gt;')
})
it('应当正确反转义HTML字符', () => {
const escaped = '&lt;div class=&quot;test&quot;&gt;Hello &amp; World&lt;/div&gt;'
const unescaped = stringUtils.htmlEscape(escaped, true)
expect(unescaped).toBe('<div class="test">Hello & World</div>')
})
})
describe('驼峰和下划线转换', () => {
describe('camelToUnderline', () => {
it('应当正确将小驼峰转换为下划线', () => {
expect(stringUtils.camelToUnderline('userName')).toBe('user_name')
expect(stringUtils.camelToUnderline('userFirstName')).toBe('user_first_name')
})
})
describe('underlineToCamel', () => {
it('应当正确将下划线转换为小驼峰', () => {
expect(stringUtils.underlineToCamel('user_name')).toBe('userName')
expect(stringUtils.underlineToCamel('user_first_name')).toBe('userFirstName')
})
})
describe('underlineToBigCamel', () => {
it('应当正确将下划线转换为大驼峰', () => {
expect(stringUtils.underlineToBigCamel('user_name')).toBe('userName')
expect(stringUtils.underlineToBigCamel('user_first_name')).toBe('userFirstName')
})
})
describe('bigCamelToUnderline', () => {
it('应当正确将大驼峰转换为下划线', () => {
expect(stringUtils.bigCamelToUnderline('UserName')).toBe('_user_name')
expect(stringUtils.bigCamelToUnderline('UserFirstName')).toBe('_user_first_name')
})
})
})
})

View File

@@ -0,0 +1,102 @@
import { describe, it, expect } from 'vitest'
import * as typeUtils from '../src/type'
describe('类型检查工具函数测试', () => {
describe('基础类型检查', () => {
it('应当正确检查数字类型', () => {
expect(typeUtils.isNumber(123)).toBe(true)
expect(typeUtils.isNumber('123')).toBe(false)
expect(typeUtils.isNumber(NaN)).toBe(true)
expect(typeUtils.isNumber(Infinity)).toBe(true)
})
it('应当正确检查字符串类型', () => {
expect(typeUtils.isString('test')).toBe(true)
expect(typeUtils.isString(123)).toBe(false)
expect(typeUtils.isString('')).toBe(true)
})
it('应当正确检查对象类型', () => {
expect(typeUtils.isObject({})).toBe(true)
expect(typeUtils.isObject([])).toBe(false)
expect(typeUtils.isObject(null)).toBe(false)
})
it('应当正确检查布尔类型', () => {
expect(typeUtils.isBoolean(true)).toBe(true)
expect(typeUtils.isBoolean(false)).toBe(true)
expect(typeUtils.isBoolean(1)).toBe(false)
})
it('应当正确检查数组类型', () => {
expect(typeUtils.isArray([])).toBe(true)
expect(typeUtils.isArray([1, 2, 3])).toBe(true)
expect(typeUtils.isArray({})).toBe(false)
})
})
describe('特殊类型检查', () => {
it('应当正确检查Promise类型', () => {
expect(typeUtils.isPromise(Promise.resolve())).toBe(true)
expect(typeUtils.isPromise({})).toBe(false)
})
it('应当正确检查函数类型', () => {
expect(typeUtils.isFunction(() => {})).toBe(true)
expect(typeUtils.isFunction(function () {})).toBe(true)
expect(typeUtils.isFunction({})).toBe(false)
})
it('应当正确检查正则表达式类型', () => {
expect(typeUtils.isRegExp(/test/)).toBe(true)
expect(typeUtils.isRegExp(new RegExp('test'))).toBe(true)
expect(typeUtils.isRegExp({})).toBe(false)
})
it('应当正确检查日期类型', () => {
expect(typeUtils.isDate(new Date())).toBe(true)
expect(typeUtils.isDate('2024-02-27')).toBe(false)
})
})
describe('空值检查', () => {
it('应当正确检查null值', () => {
expect(typeUtils.isNull(null)).toBe(true)
expect(typeUtils.isNull(undefined)).toBe(true)
expect(typeUtils.isNull(0)).toBe(false)
})
it('应当正确检查undefined值', () => {
expect(typeUtils.isUndefined(undefined)).toBe(true)
expect(typeUtils.isUndefined(null)).toBe(true)
expect(typeUtils.isUndefined(0)).toBe(false)
})
it('应当正确检查空值', () => {
expect(typeUtils.isEmpty('')).toBe(true)
expect(typeUtils.isEmpty([])).toBe(true)
expect(typeUtils.isEmpty({})).toBe(true)
expect(typeUtils.isEmpty('test')).toBe(false)
expect(typeUtils.isEmpty([1])).toBe(false)
expect(typeUtils.isEmpty({ key: 'value' })).toBe(false)
})
})
describe('类型获取和比较', () => {
it('应当正确获取值的类型', () => {
expect(typeUtils.getType(123)).toBe('Number')
expect(typeUtils.getType('test')).toBe('String')
expect(typeUtils.getType(true)).toBe('Boolean')
expect(typeUtils.getType([])).toBe('Array')
expect(typeUtils.getType({})).toBe('Object')
})
it('应当正确检查指定类型', () => {
expect(typeUtils.isType('Number', 123)).toBe(true)
expect(typeUtils.isType('String', 'test')).toBe(true)
expect(typeUtils.isType('Boolean', true)).toBe(true)
expect(typeUtils.isType('Array', [])).toBe(true)
expect(typeUtils.isType('Object', {})).toBe(true)
})
})
})