test: 添加前端 API 单元测试
- 添加 vitest 测试框架配置 - 添加 src/api/__tests__/api.test.ts: 16个 API 测试 - 修复 points.ts 和 ledger.ts 导入问题 测试覆盖: - AccountAPI: 物理账户和子账户方法 - TransactionAPI: transfer/deposit/withdraw - LedgerAPI: subjects/entry/accountEntries - ReconciliationAPI: 8个端点方法 - PointsAPI: 5个端点方法 - 类型定义和导出测试
This commit is contained in:
parent
4086cc00de
commit
4b78c43d42
10
package.json
10
package.json
@ -8,7 +8,10 @@
|
|||||||
"dev:mock": "VITE_USE_MOCK=true vite",
|
"dev:mock": "VITE_USE_MOCK=true vite",
|
||||||
"build": "vue-tsc && vite build",
|
"build": "vue-tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
@ -25,7 +28,10 @@
|
|||||||
"vite": "^5.1.4",
|
"vite": "^5.1.4",
|
||||||
"vue-tsc": "^2.0.6",
|
"vue-tsc": "^2.0.6",
|
||||||
"msw": "^2.2.1",
|
"msw": "^2.2.1",
|
||||||
"@types/node": "^20.11.19"
|
"@types/node": "^20.11.19",
|
||||||
|
"vitest": "^1.3.1",
|
||||||
|
"@vitest/coverage-v8": "^1.3.1",
|
||||||
|
"happy-dom": "^13.6.2"
|
||||||
},
|
},
|
||||||
"msw": {
|
"msw": {
|
||||||
"workerDirectory": [
|
"workerDirectory": [
|
||||||
|
|||||||
183
src/api/__tests__/api.test.ts
Normal file
183
src/api/__tests__/api.test.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* 前端 API 客户端单元测试
|
||||||
|
* 测试所有 API 模块的方法定义和类型
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect, vi } from 'vitest'
|
||||||
|
|
||||||
|
// Mock axios
|
||||||
|
vi.mock('axios', () => ({
|
||||||
|
default: {
|
||||||
|
create: vi.fn(() => ({
|
||||||
|
interceptors: {
|
||||||
|
request: { use: vi.fn() },
|
||||||
|
response: { use: vi.fn() }
|
||||||
|
},
|
||||||
|
get: vi.fn(),
|
||||||
|
post: vi.fn(),
|
||||||
|
put: vi.fn(),
|
||||||
|
delete: vi.fn()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
// ==================== Account API 测试 ====================
|
||||||
|
describe('AccountAPI', () => {
|
||||||
|
it('should export AccountAPI class', async () => {
|
||||||
|
const { AccountAPI } = await import('../account')
|
||||||
|
expect(AccountAPI).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have physical account methods', async () => {
|
||||||
|
const { AccountAPI } = await import('../account')
|
||||||
|
|
||||||
|
// 实体账户相关方法
|
||||||
|
expect(typeof AccountAPI.createPhysicalAccount).toBe('function')
|
||||||
|
expect(typeof AccountAPI.getPhysicalAccounts).toBe('function')
|
||||||
|
expect(typeof AccountAPI.getPhysicalAccount).toBe('function')
|
||||||
|
expect(typeof AccountAPI.freezePhysicalAccount).toBe('function')
|
||||||
|
expect(typeof AccountAPI.unfreezePhysicalAccount).toBe('function')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have sub account methods', async () => {
|
||||||
|
const { AccountAPI } = await import('../account')
|
||||||
|
|
||||||
|
// 虚拟子账户相关方法
|
||||||
|
expect(typeof AccountAPI.createSubAccount).toBe('function')
|
||||||
|
expect(typeof AccountAPI.getSubAccount).toBe('function')
|
||||||
|
expect(typeof AccountAPI.getSubAccountBalance).toBe('function')
|
||||||
|
expect(typeof AccountAPI.freezeSubAccount).toBe('function')
|
||||||
|
expect(typeof AccountAPI.unfreezeSubAccount).toBe('function')
|
||||||
|
expect(typeof AccountAPI.closeSubAccount).toBe('function')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== Transaction API 测试 ====================
|
||||||
|
describe('TransactionAPI', () => {
|
||||||
|
it('should export TransactionAPI class', async () => {
|
||||||
|
const { TransactionAPI } = await import('../transaction')
|
||||||
|
expect(TransactionAPI).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have all required methods', async () => {
|
||||||
|
const { TransactionAPI } = await import('../transaction')
|
||||||
|
|
||||||
|
// 5 个核心交易 API 方法
|
||||||
|
expect(typeof TransactionAPI.transfer).toBe('function')
|
||||||
|
expect(typeof TransactionAPI.deposit).toBe('function')
|
||||||
|
expect(typeof TransactionAPI.withdraw).toBe('function')
|
||||||
|
expect(typeof TransactionAPI.getTransaction).toBe('function')
|
||||||
|
expect(typeof TransactionAPI.getTransactions).toBe('function')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have DepositRequest and WithdrawRequest types', async () => {
|
||||||
|
// 类型检查 - 如果编译通过则测试通过
|
||||||
|
const { TransactionAPI } = await import('../transaction')
|
||||||
|
expect(TransactionAPI.deposit).toBeDefined()
|
||||||
|
expect(TransactionAPI.withdraw).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== Ledger API 测试 ====================
|
||||||
|
describe('LedgerAPI', () => {
|
||||||
|
it('should export LedgerAPI class', async () => {
|
||||||
|
const { LedgerAPI } = await import('../ledger')
|
||||||
|
expect(LedgerAPI).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have all required methods', async () => {
|
||||||
|
const { LedgerAPI } = await import('../ledger')
|
||||||
|
|
||||||
|
// 3 个账务 API 方法
|
||||||
|
expect(typeof LedgerAPI.getSubjects).toBe('function')
|
||||||
|
expect(typeof LedgerAPI.getEntry).toBe('function')
|
||||||
|
expect(typeof LedgerAPI.getAccountEntries).toBe('function')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== Reconciliation API 测试 ====================
|
||||||
|
describe('ReconciliationAPI', () => {
|
||||||
|
it('should export ReconciliationAPI class', async () => {
|
||||||
|
const { ReconciliationAPI } = await import('../reconciliation')
|
||||||
|
expect(ReconciliationAPI).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have all required methods', async () => {
|
||||||
|
const { ReconciliationAPI } = await import('../reconciliation')
|
||||||
|
|
||||||
|
// 8 个对账 API 方法
|
||||||
|
expect(typeof ReconciliationAPI.runReconciliation).toBe('function')
|
||||||
|
expect(typeof ReconciliationAPI.getBatch).toBe('function')
|
||||||
|
expect(typeof ReconciliationAPI.getBatchItems).toBe('function')
|
||||||
|
expect(typeof ReconciliationAPI.verifyThreeAccounts).toBe('function')
|
||||||
|
expect(typeof ReconciliationAPI.createAdjustment).toBe('function')
|
||||||
|
expect(typeof ReconciliationAPI.approveAdjustment).toBe('function')
|
||||||
|
expect(typeof ReconciliationAPI.rejectAdjustment).toBe('function')
|
||||||
|
expect(typeof ReconciliationAPI.getPendingAdjustments).toBe('function')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should export status label mappings', async () => {
|
||||||
|
const {
|
||||||
|
ReconciliationStatusLabels,
|
||||||
|
ReconciliationItemStatusLabels,
|
||||||
|
AdjustmentTypeLabels,
|
||||||
|
AdjustmentStatusLabels
|
||||||
|
} = await import('../reconciliation')
|
||||||
|
|
||||||
|
expect(ReconciliationStatusLabels).toBeDefined()
|
||||||
|
expect(ReconciliationItemStatusLabels).toBeDefined()
|
||||||
|
expect(AdjustmentTypeLabels).toBeDefined()
|
||||||
|
expect(AdjustmentStatusLabels).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== Points API 测试 ====================
|
||||||
|
describe('PointsAPI', () => {
|
||||||
|
it('should export PointsAPI class', async () => {
|
||||||
|
const { PointsAPI } = await import('../points')
|
||||||
|
expect(PointsAPI).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have all required methods', async () => {
|
||||||
|
const { PointsAPI } = await import('../points')
|
||||||
|
|
||||||
|
// 5 个积分 API 方法
|
||||||
|
expect(typeof PointsAPI.getAccounts).toBe('function')
|
||||||
|
expect(typeof PointsAPI.earnPoints).toBe('function')
|
||||||
|
expect(typeof PointsAPI.spendPoints).toBe('function')
|
||||||
|
expect(typeof PointsAPI.transferPoints).toBe('function')
|
||||||
|
expect(typeof PointsAPI.getTransactions).toBe('function')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== Types 测试 ====================
|
||||||
|
describe('Type Definitions', () => {
|
||||||
|
it('should export points types', async () => {
|
||||||
|
const types = await import('@/types/points')
|
||||||
|
expect(types.PointsTypeLabels).toBeDefined()
|
||||||
|
expect(types.PointsTransactionTypeLabels).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should export ledger types', async () => {
|
||||||
|
const types = await import('@/types/ledger')
|
||||||
|
expect(types.SubjectCategoryLabels).toBeDefined()
|
||||||
|
expect(types.DirectionLabels).toBeDefined()
|
||||||
|
expect(types.EntryStatusLabels).toBeDefined()
|
||||||
|
expect(types.PredefinedSubjects).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== API Index 导出测试 ====================
|
||||||
|
describe('API Index Exports', () => {
|
||||||
|
it('should export all API modules from index', async () => {
|
||||||
|
const api = await import('../index')
|
||||||
|
|
||||||
|
expect(api.AccountAPI).toBeDefined()
|
||||||
|
expect(api.TransactionAPI).toBeDefined()
|
||||||
|
expect(api.ReconciliationAPI).toBeDefined()
|
||||||
|
expect(api.PointsAPI).toBeDefined()
|
||||||
|
expect(api.LedgerAPI).toBeDefined()
|
||||||
|
expect(api.apiClient).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
* 账务 API 客户端
|
* 账务 API 客户端
|
||||||
* 提供会计科目、分录查询等功能
|
* 提供会计科目、分录查询等功能
|
||||||
*/
|
*/
|
||||||
import apiClient from './client'
|
import { apiClient } from './client'
|
||||||
import type {
|
import type {
|
||||||
AccountingSubject,
|
AccountingSubject,
|
||||||
LedgerEntry,
|
LedgerEntry,
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
* 积分 API 客户端
|
* 积分 API 客户端
|
||||||
* 提供积分账户管理、积分获取/消费/转移等功能
|
* 提供积分账户管理、积分获取/消费/转移等功能
|
||||||
*/
|
*/
|
||||||
import apiClient from './client'
|
import { apiClient } from './client'
|
||||||
import type {
|
import type {
|
||||||
PointsAccount,
|
PointsAccount,
|
||||||
PointsTransaction,
|
PointsTransaction,
|
||||||
|
|||||||
24
vitest.config.ts
Normal file
24
vitest.config.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'happy-dom',
|
||||||
|
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
reporter: ['text', 'json', 'html'],
|
||||||
|
include: ['src/**/*.ts'],
|
||||||
|
exclude: ['src/**/*.d.ts', 'src/**/*.test.ts', 'src/mocks/**'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user