20251222
This commit is contained in:
245
tests/frontend/CheckIn.test.ts
Normal file
245
tests/frontend/CheckIn.test.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* CheckIn组件测试
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import CheckIn from '../../../src/view/CheckIn.vue'
|
||||
import axios from 'axios'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
|
||||
vi.mock('axios')
|
||||
vi.mock('@arco-design/web-vue', () => ({
|
||||
Message: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
const mockRouter = {
|
||||
push: vi.fn()
|
||||
}
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => mockRouter
|
||||
}))
|
||||
|
||||
describe('CheckIn', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
localStorage.setItem('username', 'testuser')
|
||||
})
|
||||
|
||||
it('应该正确渲染组件', () => {
|
||||
const wrapper = mount(CheckIn)
|
||||
expect(wrapper.find('.checkin-page').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('应该显示签到状态', async () => {
|
||||
const mockStatus = {
|
||||
today_checked: false,
|
||||
consecutive_days: 7,
|
||||
total_days: 30
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockStatus }
|
||||
})
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.text()).toContain('7')
|
||||
expect(wrapper.text()).toContain('30')
|
||||
})
|
||||
|
||||
it('未签到时应该显示签到按钮', async () => {
|
||||
const mockStatus = {
|
||||
today_checked: false,
|
||||
consecutive_days: 5,
|
||||
total_days: 20
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockStatus }
|
||||
})
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.text()).toContain('立即签到')
|
||||
})
|
||||
|
||||
it('已签到时应该显示已签到状态', async () => {
|
||||
const mockStatus = {
|
||||
today_checked: true,
|
||||
consecutive_days: 8,
|
||||
total_days: 25
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockStatus }
|
||||
})
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.text()).toContain('今日已签到')
|
||||
})
|
||||
|
||||
it('点击签到按钮应该调用签到API', async () => {
|
||||
const mockStatus = {
|
||||
today_checked: false,
|
||||
consecutive_days: 5,
|
||||
total_days: 20
|
||||
}
|
||||
|
||||
const mockCheckInResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
data: {
|
||||
success: true,
|
||||
points_earned: 30,
|
||||
consecutive_days: 6,
|
||||
message: '签到成功'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockStatus }
|
||||
})
|
||||
;(axios.post as any).mockResolvedValue(mockCheckInResponse)
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
const checkinButton = wrapper.find('.checkin-button')
|
||||
if (checkinButton.exists()) {
|
||||
await checkinButton.trigger('click')
|
||||
|
||||
expect(axios.post).toHaveBeenCalledWith(
|
||||
expect.stringContaining('/checkin/daily'),
|
||||
expect.objectContaining({
|
||||
username: 'testuser'
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('签到成功后应该显示成功提示', async () => {
|
||||
const mockCheckInResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
data: {
|
||||
success: true,
|
||||
points_earned: 30,
|
||||
consecutive_days: 6,
|
||||
message: '签到成功!连续签到6天,获得30积分'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;(axios.post as any).mockResolvedValue(mockCheckInResponse)
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
const checkinButton = wrapper.find('.checkin-button')
|
||||
if (checkinButton.exists()) {
|
||||
await checkinButton.trigger('click')
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(Message.success).toHaveBeenCalled()
|
||||
}
|
||||
})
|
||||
|
||||
it('应该显示奖励规则', async () => {
|
||||
const mockRewards = [
|
||||
{ consecutive_days: 1, base_points: 10, bonus_points: 0, total_points: 10 },
|
||||
{ consecutive_days: 7, base_points: 10, bonus_points: 20, total_points: 30 }
|
||||
]
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: mockRewards
|
||||
})
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
|
||||
expect(wrapper.text()).toContain('奖励规则')
|
||||
})
|
||||
|
||||
it('应该显示签到日历', async () => {
|
||||
const mockCalendar = {
|
||||
year: 2024,
|
||||
month: 1,
|
||||
checked_dates: [1, 5, 10, 15, 20]
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockCalendar }
|
||||
})
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
|
||||
expect(wrapper.find('.calendar-grid').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('应该能够切换月份', async () => {
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
const prevButton = wrapper.find('.calendar-title').parentElement?.querySelector('button')
|
||||
if (prevButton) {
|
||||
await prevButton.dispatchEvent(new Event('click'))
|
||||
// 应该调用API加载新月份的数据
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
}
|
||||
})
|
||||
|
||||
it('应该显示签到历史记录', async () => {
|
||||
const mockHistory = {
|
||||
total: 10,
|
||||
records: [
|
||||
{
|
||||
check_in_date: '2024-01-01',
|
||||
points_earned: 30,
|
||||
consecutive_days: 7
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockHistory }
|
||||
})
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
|
||||
expect(wrapper.text()).toContain('签到历史')
|
||||
})
|
||||
|
||||
it('重复签到应该显示错误提示', async () => {
|
||||
;(axios.post as any).mockRejectedValue({
|
||||
response: {
|
||||
data: {
|
||||
detail: '今日已签到,明天再来吧'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const wrapper = mount(CheckIn)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
const checkinButton = wrapper.find('.checkin-button')
|
||||
if (checkinButton.exists()) {
|
||||
await checkinButton.trigger('click')
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(Message.error).toHaveBeenCalled()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
210
tests/frontend/HomePage.test.ts
Normal file
210
tests/frontend/HomePage.test.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* HomePage组件测试
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import HomePage from '../../../src/view/HomePage.vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import axios from 'axios'
|
||||
|
||||
// Mock axios
|
||||
vi.mock('axios')
|
||||
|
||||
// Mock Arco Design Message
|
||||
vi.mock('@arco-design/web-vue', () => ({
|
||||
Message: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warning: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
// Mock router
|
||||
const mockRouter = {
|
||||
push: vi.fn()
|
||||
}
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => mockRouter
|
||||
}))
|
||||
|
||||
describe('HomePage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
localStorage.setItem('username', 'testuser')
|
||||
localStorage.setItem('token', 'test_token')
|
||||
})
|
||||
|
||||
it('应该正确渲染组件', () => {
|
||||
const wrapper = mount(HomePage)
|
||||
expect(wrapper.find('.home-page').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('应该显示欢迎信息', async () => {
|
||||
// Mock API响应
|
||||
const mockUserInfo = {
|
||||
username: 'testuser',
|
||||
nickname: '测试用户',
|
||||
points: 1000,
|
||||
vip_type: 'vip',
|
||||
consecutive_check_in: 7
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValueOnce({
|
||||
data: {
|
||||
code: 200,
|
||||
data: mockUserInfo
|
||||
}
|
||||
})
|
||||
|
||||
const wrapper = mount(HomePage)
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
// 等待数据加载
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.text()).toContain('测试用户')
|
||||
})
|
||||
|
||||
it('应该显示正确的积分数量', async () => {
|
||||
const mockUserInfo = {
|
||||
username: 'testuser',
|
||||
points: 1500,
|
||||
vip_type: 'none',
|
||||
consecutive_check_in: 3
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValueOnce({
|
||||
data: {
|
||||
code: 200,
|
||||
data: mockUserInfo
|
||||
}
|
||||
})
|
||||
|
||||
const wrapper = mount(HomePage)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
// 应该显示1500积分
|
||||
expect(wrapper.html()).toContain('1500')
|
||||
})
|
||||
|
||||
it('应该显示VIP状态', async () => {
|
||||
const mockUserInfo = {
|
||||
username: 'testuser',
|
||||
points: 1000,
|
||||
vip_type: 'svip',
|
||||
consecutive_check_in: 10
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValueOnce({
|
||||
data: {
|
||||
code: 200,
|
||||
data: mockUserInfo
|
||||
}
|
||||
})
|
||||
|
||||
const wrapper = mount(HomePage)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.text()).toContain('SVIP')
|
||||
})
|
||||
|
||||
it('点击功能卡片应该调用使用功能API', async () => {
|
||||
const mockUserInfo = {
|
||||
username: 'testuser',
|
||||
points: 1000,
|
||||
vip_type: 'none',
|
||||
consecutive_check_in: 5
|
||||
}
|
||||
|
||||
const mockFeatureResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
data: {
|
||||
success: true,
|
||||
cost_type: 'points',
|
||||
points_cost: 50,
|
||||
points_remaining: 950,
|
||||
message: '使用成功'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValueOnce({
|
||||
data: { code: 200, data: mockUserInfo }
|
||||
})
|
||||
;(axios.post as any).mockResolvedValueOnce(mockFeatureResponse)
|
||||
|
||||
const wrapper = mount(HomePage)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
// 模拟点击功能卡片
|
||||
const featureItem = wrapper.find('.feature-item')
|
||||
if (featureItem.exists()) {
|
||||
await featureItem.trigger('click')
|
||||
|
||||
expect(axios.post).toHaveBeenCalledWith(
|
||||
expect.stringContaining('/feature/use'),
|
||||
expect.objectContaining({
|
||||
username: 'testuser',
|
||||
feature_key: expect.any(String),
|
||||
device_id: expect.any(String)
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('积分不足时应该显示错误提示', async () => {
|
||||
const mockUserInfo = {
|
||||
username: 'testuser',
|
||||
points: 10,
|
||||
vip_type: 'none',
|
||||
consecutive_check_in: 1
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValueOnce({
|
||||
data: { code: 200, data: mockUserInfo }
|
||||
})
|
||||
;(axios.post as any).mockRejectedValueOnce({
|
||||
response: {
|
||||
data: {
|
||||
detail: '积分不足'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const wrapper = mount(HomePage)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
const featureItem = wrapper.find('.feature-item')
|
||||
if (featureItem.exists()) {
|
||||
await featureItem.trigger('click')
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(Message.error).toHaveBeenCalled()
|
||||
}
|
||||
})
|
||||
|
||||
it('应该有快捷入口按钮', async () => {
|
||||
const wrapper = mount(HomePage)
|
||||
|
||||
expect(wrapper.text()).toContain('每日签到')
|
||||
expect(wrapper.text()).toContain('个人中心')
|
||||
expect(wrapper.text()).toContain('工作台')
|
||||
})
|
||||
|
||||
it('未登录时应该跳转到登录页', async () => {
|
||||
localStorage.removeItem('username')
|
||||
localStorage.removeItem('token')
|
||||
|
||||
;(axios.get as any).mockRejectedValueOnce(new Error('Unauthorized'))
|
||||
|
||||
const wrapper = mount(HomePage)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
// 应该尝试跳转到登录页
|
||||
expect(mockRouter.push).toHaveBeenCalledWith('/login')
|
||||
})
|
||||
})
|
||||
|
||||
180
tests/frontend/Profile.test.ts
Normal file
180
tests/frontend/Profile.test.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Profile组件测试
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Profile from '../../../src/view/Profile.vue'
|
||||
import axios from 'axios'
|
||||
|
||||
vi.mock('axios')
|
||||
|
||||
const mockRouter = {
|
||||
push: vi.fn()
|
||||
}
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => mockRouter
|
||||
}))
|
||||
|
||||
describe('Profile', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
localStorage.setItem('username', 'testuser')
|
||||
localStorage.setItem('token', 'test_token')
|
||||
})
|
||||
|
||||
it('应该正确渲染组件', () => {
|
||||
const wrapper = mount(Profile)
|
||||
expect(wrapper.find('.profile-page').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('应该显示用户基本信息', async () => {
|
||||
const mockProfile = {
|
||||
username: 'testuser',
|
||||
nickname: '测试昵称',
|
||||
email: 'test@example.com',
|
||||
points: 1200,
|
||||
level: 3,
|
||||
vip_type: 'vip',
|
||||
total_check_in_days: 30,
|
||||
consecutive_check_in: 7
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValueOnce({
|
||||
data: { code: 200, data: mockProfile }
|
||||
}).mockResolvedValueOnce({
|
||||
data: { code: 200, data: { total: 0, current_balance: 1200, records: [] } }
|
||||
})
|
||||
|
||||
const wrapper = mount(Profile)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.text()).toContain('测试昵称')
|
||||
expect(wrapper.text()).toContain('testuser')
|
||||
})
|
||||
|
||||
it('应该显示统计数据卡片', async () => {
|
||||
const mockProfile = {
|
||||
username: 'testuser',
|
||||
points: 1200,
|
||||
total_check_in_days: 30,
|
||||
consecutive_check_in: 7,
|
||||
vip_type: 'vip'
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockProfile }
|
||||
})
|
||||
|
||||
const wrapper = mount(Profile)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.findAll('.stat-card').length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('应该能够编辑资料', async () => {
|
||||
const mockProfile = {
|
||||
username: 'testuser',
|
||||
nickname: '旧昵称',
|
||||
points: 1000
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockProfile }
|
||||
})
|
||||
;(axios.put as any).mockResolvedValue({
|
||||
data: { code: 200, message: '更新成功' }
|
||||
})
|
||||
|
||||
const wrapper = mount(Profile)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
// 模拟点击编辑按钮
|
||||
const editButton = wrapper.find('button')
|
||||
if (editButton.exists() && editButton.text().includes('编辑')) {
|
||||
await editButton.trigger('click')
|
||||
// 编辑对话框应该打开
|
||||
await wrapper.vm.$nextTick()
|
||||
}
|
||||
})
|
||||
|
||||
it('应该显示VIP徽章', async () => {
|
||||
const mockProfile = {
|
||||
username: 'testuser',
|
||||
vip_type: 'svip',
|
||||
points: 2000
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockProfile }
|
||||
})
|
||||
|
||||
const wrapper = mount(Profile)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.html()).toContain('SVIP')
|
||||
})
|
||||
|
||||
it('应该显示积分历史列表', async () => {
|
||||
const mockProfile = {
|
||||
username: 'testuser',
|
||||
points: 1000
|
||||
}
|
||||
|
||||
const mockHistory = {
|
||||
total: 10,
|
||||
current_balance: 1000,
|
||||
records: [
|
||||
{
|
||||
type: 'checkin',
|
||||
amount: 30,
|
||||
balance: 1000,
|
||||
description: '每日签到',
|
||||
created_at: '2024-01-01T10:00:00'
|
||||
},
|
||||
{
|
||||
type: 'consume',
|
||||
amount: -50,
|
||||
balance: 970,
|
||||
description: '使用AI配色',
|
||||
created_at: '2024-01-01T09:00:00'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
;(axios.get as any)
|
||||
.mockResolvedValueOnce({ data: { code: 200, data: mockProfile } })
|
||||
.mockResolvedValueOnce({ data: { code: 200, data: mockHistory } })
|
||||
|
||||
const wrapper = mount(Profile)
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
|
||||
expect(wrapper.text()).toContain('每日签到')
|
||||
expect(wrapper.text()).toContain('使用AI配色')
|
||||
})
|
||||
|
||||
it('积分变动应该有不同颜色', async () => {
|
||||
const mockHistory = {
|
||||
total: 2,
|
||||
current_balance: 1000,
|
||||
records: [
|
||||
{ type: 'checkin', amount: 30, balance: 1000, description: '签到', created_at: '2024-01-01' },
|
||||
{ type: 'consume', amount: -50, balance: 970, description: '消费', created_at: '2024-01-01' }
|
||||
]
|
||||
}
|
||||
|
||||
;(axios.get as any).mockResolvedValue({
|
||||
data: { code: 200, data: mockHistory }
|
||||
})
|
||||
|
||||
const wrapper = mount(Profile)
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
|
||||
// 正数应该是绿色,负数应该是红色
|
||||
const html = wrapper.html()
|
||||
expect(html).toContain('positive')
|
||||
expect(html).toContain('negative')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user