diff --git a/novalon-manage-web/src/__tests__/components/ConfigManagement.test.ts b/novalon-manage-web/src/__tests__/components/ConfigManagement.test.ts
deleted file mode 100644
index 796c0ee..0000000
--- a/novalon-manage-web/src/__tests__/components/ConfigManagement.test.ts
+++ /dev/null
@@ -1,267 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import ConfigManagement from '@/views/config/ConfigManagement.vue'
-
-vi.mock('vue-router')
-vi.mock('element-plus', () => ({
- ElMessage: {
- success: vi.fn(),
- error: vi.fn(),
- },
- ElMessageBox: {
- confirm: vi.fn(),
- },
-}))
-
-vi.mock('@/utils/request', () => {
- const mockRequest = {
- get: vi.fn(),
- post: vi.fn(),
- put: vi.fn(),
- delete: vi.fn(),
- }
-
- mockRequest.get.mockResolvedValue([])
- mockRequest.post.mockResolvedValue({})
- mockRequest.put.mockResolvedValue({})
- mockRequest.delete.mockResolvedValue({})
-
- return {
- default: mockRequest,
- }
-})
-
-describe('ConfigManagement Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: '
Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render config management container', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.find('.config-management').exists()).toBe(true)
- })
-
- it('should initialize with empty data source', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.dataSource).toBeDefined()
- expect(Array.isArray(wrapper.vm.dataSource)).toBe(true)
- })
-
- it('should initialize with loading state false', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.loading).toBeDefined()
- expect(typeof wrapper.vm.loading).toBe('boolean')
- })
-
- it('should initialize with modal visible false', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.modalVisible).toBe(false)
- })
-
- it('should initialize with empty form state', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.formState.configName).toBe('')
- expect(wrapper.vm.formState.configKey).toBe('')
- expect(wrapper.vm.formState.configValue).toBe('')
- })
- })
-
- describe('add config functionality', () => {
- it('should have handleAdd method', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleAdd).toBe('function')
- })
- })
-
- describe('edit config functionality', () => {
- it('should have handleEdit method', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleEdit).toBe('function')
- })
- })
-
- describe('delete config functionality', () => {
- it('should have handleDelete method', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleDelete).toBe('function')
- })
- })
-
- describe('form submission', () => {
- it('should have handleModalOk method', () => {
- wrapper = mount(ConfigManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleModalOk).toBe('function')
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/Dashboard.test.ts b/novalon-manage-web/src/__tests__/components/Dashboard.test.ts
deleted file mode 100644
index 3cba938..0000000
--- a/novalon-manage-web/src/__tests__/components/Dashboard.test.ts
+++ /dev/null
@@ -1,261 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import Dashboard from '@/views/system/Dashboard.vue'
-
-vi.mock('vue-router')
-vi.mock('@/api/user.api.ts', () => ({
- getUserStats: vi.fn(),
- getRecentLogins: vi.fn(),
-}))
-
-describe('Dashboard Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Dashboard
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render dashboard container', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.find('.dashboard').exists()).toBe(true)
- })
-
- it('should initialize with loading state', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.loading).toBe(true)
- })
-
- it('should initialize with empty stats', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.stats).toEqual({
- userCount: 0,
- roleCount: 0,
- todayLogin: 0,
- operationLog: 0,
- })
- })
- })
-
- describe('statistics cards', () => {
- it('should render user count card', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.stats.userCount).toBeDefined()
- })
-
- it('should render role count card', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.stats.roleCount).toBeDefined()
- })
-
- it('should render today login card', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.stats.todayLogin).toBeDefined()
- })
-
- it('should render operation log card', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.stats.operationLog).toBeDefined()
- })
- })
-
- describe('recent logins', () => {
- it('should initialize with empty recent logins', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.recentLogins).toEqual([])
- })
-
- it('should display empty state when no recent logins', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.recentLogins.length).toBe(0)
- })
- })
-
- describe('data loading', () => {
- it('should set loading to false after data loaded', async () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.vm.loading).toBe(true)
-
- wrapper.vm.loading = false
- await wrapper.vm.$nextTick()
-
- expect(wrapper.vm.loading).toBe(false)
- })
- })
-
- describe('document title', () => {
- it('should have dashboard component mounted', () => {
- wrapper = mount(Dashboard, {
- global: {
- plugins: [router],
- stubs: {
- 'el-row': true,
- 'el-col': true,
- 'el-card': true,
- 'el-statistic': true,
- 'el-icon': true,
- 'el-timeline': true,
- 'el-timeline-item': true,
- },
- },
- })
-
- expect(wrapper.exists()).toBe(true)
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/DictManagement.test.ts b/novalon-manage-web/src/__tests__/components/DictManagement.test.ts
deleted file mode 100644
index 1318902..0000000
--- a/novalon-manage-web/src/__tests__/components/DictManagement.test.ts
+++ /dev/null
@@ -1,286 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import DictManagement from '@/views/config/DictManagement.vue'
-
-vi.mock('vue-router')
-vi.mock('element-plus', () => ({
- ElMessage: {
- success: vi.fn(),
- error: vi.fn(),
- },
- ElMessageBox: {
- confirm: vi.fn(),
- },
-}))
-
-vi.mock('@/utils/request', () => {
- const mockRequest = {
- get: vi.fn(),
- post: vi.fn(),
- put: vi.fn(),
- delete: vi.fn(),
- }
-
- mockRequest.get.mockResolvedValue([])
- mockRequest.post.mockResolvedValue({})
- mockRequest.put.mockResolvedValue({})
- mockRequest.delete.mockResolvedValue({})
-
- return {
- default: mockRequest,
- }
-})
-
-describe('DictManagement Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render dict management container', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.find('.dict-management').exists()).toBe(true)
- })
-
- it('should initialize with empty data source', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.dataSource).toBeDefined()
- expect(Array.isArray(wrapper.vm.dataSource)).toBe(true)
- })
-
- it('should initialize with loading state false', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.loading).toBeDefined()
- expect(typeof wrapper.vm.loading).toBe('boolean')
- })
-
- it('should initialize with modal visible false', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.modalVisible).toBe(false)
- })
-
- it('should initialize with empty form state', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.formState.dictName).toBe('')
- expect(wrapper.vm.formState.dictType).toBe('')
- expect(wrapper.vm.formState.status).toBe('0')
- expect(wrapper.vm.formState.remark).toBe('')
- })
- })
-
- describe('add dict functionality', () => {
- it('should have handleAdd method', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleAdd).toBe('function')
- })
- })
-
- describe('edit dict functionality', () => {
- it('should have handleEdit method', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleEdit).toBe('function')
- })
- })
-
- describe('delete dict functionality', () => {
- it('should have handleDelete method', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleDelete).toBe('function')
- })
- })
-
- describe('form submission', () => {
- it('should have handleModalOk method', () => {
- wrapper = mount(DictManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleModalOk).toBe('function')
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/ExceptionLog.test.ts b/novalon-manage-web/src/__tests__/components/ExceptionLog.test.ts
deleted file mode 100644
index 63e5225..0000000
--- a/novalon-manage-web/src/__tests__/components/ExceptionLog.test.ts
+++ /dev/null
@@ -1,257 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import ExceptionLog from '@/views/audit/ExceptionLog.vue'
-
-vi.mock('vue-router')
-vi.mock('@/api/exceptionLog', () => ({
- exceptionLogApi: {
- getPage: vi.fn().mockResolvedValue({
- content: [
- { id: 1, username: 'admin', operation: '用户登录', method: 'POST /api/auth/login', errorMsg: 'NullPointerException', ip: '192.168.1.1', createTime: '2026-01-01T10:00:00' },
- { id: 2, username: 'user', operation: '文件上传', method: 'POST /api/files/upload', errorMsg: 'FileSizeLimitExceededException', ip: '192.168.1.2', createTime: '2026-01-02T11:00:00' },
- ],
- totalElements: 2,
- }),
- },
-}))
-
-describe('ExceptionLog Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render exception log container', () => {
- wrapper = mount(ExceptionLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-descriptions': true,
- 'el-descriptions-item': true,
- },
- },
- })
-
- expect(wrapper.find('.exception-log').exists()).toBe(true)
- })
-
- it('should initialize with empty search keyword', () => {
- wrapper = mount(ExceptionLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-descriptions': true,
- 'el-descriptions-item': true,
- },
- },
- })
-
- expect(wrapper.vm.searchKeyword).toBe('')
- })
-
- it('should initialize with correct pagination defaults', () => {
- wrapper = mount(ExceptionLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-descriptions': true,
- 'el-descriptions-item': true,
- },
- },
- })
-
- expect(wrapper.vm.pagination.current).toBe(1)
- expect(wrapper.vm.pagination.pageSize).toBe(10)
- expect(wrapper.vm.pagination.total).toBe(0)
- })
-
- it('should initialize with hidden detail dialog', () => {
- wrapper = mount(ExceptionLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-descriptions': true,
- 'el-descriptions-item': true,
- },
- },
- })
-
- expect(wrapper.vm.detailVisible).toBe(false)
- })
- })
-
- describe('detail view handling', () => {
- beforeEach(() => {
- wrapper = mount(ExceptionLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-descriptions': true,
- 'el-descriptions-item': true,
- },
- },
- })
- })
-
- it('should show detail dialog when viewing exception', () => {
- const exception = {
- id: 1,
- username: 'admin',
- operation: '用户登录',
- method: 'POST /api/auth/login',
- errorMsg: 'NullPointerException',
- ip: '192.168.1.1',
- createTime: '2026-01-01T10:00:00',
- }
-
- wrapper.vm.handleViewDetail(exception)
-
- expect(wrapper.vm.detailVisible).toBe(true)
- expect(wrapper.vm.currentDetail).toEqual(exception)
- })
-
- it('should create a copy of exception data for detail view', () => {
- const exception = {
- id: 1,
- username: 'admin',
- }
-
- wrapper.vm.handleViewDetail(exception)
- wrapper.vm.currentDetail.username = 'modified'
-
- expect(exception.username).toBe('admin')
- })
- })
-
- describe('sort handling', () => {
- beforeEach(() => {
- wrapper = mount(ExceptionLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-descriptions': true,
- 'el-descriptions-item': true,
- },
- },
- })
- })
-
- it('should update sort info on ascending order', () => {
- wrapper.vm.handleSortChange({ prop: 'username', order: 'ascending' })
- expect(wrapper.vm.sortInfo.sort).toBe('username')
- expect(wrapper.vm.sortInfo.order).toBe('asc')
- })
-
- it('should update sort info on descending order', () => {
- wrapper.vm.handleSortChange({ prop: 'createTime', order: 'descending' })
- expect(wrapper.vm.sortInfo.sort).toBe('createTime')
- expect(wrapper.vm.sortInfo.order).toBe('desc')
- })
- })
-
- describe('pagination handling', () => {
- beforeEach(() => {
- wrapper = mount(ExceptionLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-descriptions': true,
- 'el-descriptions-item': true,
- },
- },
- })
- })
-
- it('should reset to first page on size change', () => {
- wrapper.vm.pagination.current = 5
- wrapper.vm.handleSizeChange()
- expect(wrapper.vm.pagination.current).toBe(1)
- })
-
- it('should reset to first page on search', () => {
- wrapper.vm.pagination.current = 5
- wrapper.vm.handleSearch()
- expect(wrapper.vm.pagination.current).toBe(1)
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/FileManagement.test.ts b/novalon-manage-web/src/__tests__/components/FileManagement.test.ts
deleted file mode 100644
index ac5ee93..0000000
--- a/novalon-manage-web/src/__tests__/components/FileManagement.test.ts
+++ /dev/null
@@ -1,247 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import FileManagement from '@/views/file/FileManagement.vue'
-
-vi.mock('vue-router')
-vi.mock('element-plus', () => ({
- ElMessage: {
- success: vi.fn(),
- error: vi.fn(),
- },
- ElMessageBox: {
- confirm: vi.fn(),
- },
-}))
-
-vi.mock('@/utils/request', () => {
- const mockRequest = {
- get: vi.fn(),
- post: vi.fn(),
- put: vi.fn(),
- delete: vi.fn(),
- }
-
- mockRequest.get.mockResolvedValue([
- { id: 1, fileName: 'test.pdf', fileSize: 1024, fileType: 'application/pdf', storageType: 'local', createdAt: '2026-01-01', createBy: 'admin' },
- { id: 2, fileName: 'image.png', fileSize: 2048, fileType: 'image/png', storageType: 'local', createdAt: '2026-01-02', createBy: 'user' },
- ])
- mockRequest.post.mockResolvedValue({})
- mockRequest.put.mockResolvedValue({})
- mockRequest.delete.mockResolvedValue({})
-
- return {
- default: mockRequest,
- }
-})
-
-describe('FileManagement Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render file management container', () => {
- wrapper = mount(FileManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-upload': true,
- 'el-tag': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.find('.file-management').exists()).toBe(true)
- })
-
- it('should initialize with empty search keyword', () => {
- wrapper = mount(FileManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-upload': true,
- 'el-tag': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.searchKeyword).toBe('')
- })
-
- it('should initialize with loading state false before data fetch', async () => {
- wrapper = mount(FileManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-upload': true,
- 'el-tag': true,
- 'el-icon': true,
- },
- },
- })
-
- await wrapper.vm.$nextTick()
- expect([true, false]).toContain(wrapper.vm.loading)
- })
- })
-
- describe('file type utilities', () => {
- beforeEach(() => {
- wrapper = mount(FileManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-upload': true,
- 'el-tag': true,
- 'el-icon': true,
- },
- },
- })
- })
-
- it('should return correct file type name for images', () => {
- expect(wrapper.vm.getFileTypeName('image/png')).toBe('图片')
- expect(wrapper.vm.getFileTypeName('image/jpeg')).toBe('图片')
- })
-
- it('should return correct file type name for videos', () => {
- expect(wrapper.vm.getFileTypeName('video/mp4')).toBe('视频')
- })
-
- it('should return correct file type name for audio', () => {
- expect(wrapper.vm.getFileTypeName('audio/mp3')).toBe('音频')
- })
-
- it('should return correct file type name for PDF', () => {
- expect(wrapper.vm.getFileTypeName('application/pdf')).toBe('PDF')
- })
-
- it('should return correct file type name for Word', () => {
- expect(wrapper.vm.getFileTypeName('application/vnd.openxmlformats-officedocument.wordprocessingml.document')).toBe('Word')
- })
-
- it('should return correct file type name for Excel', () => {
- expect(wrapper.vm.getFileTypeName('application/vnd.ms-excel')).toBe('Excel')
- })
-
- it('should return unknown for unknown file types', () => {
- expect(wrapper.vm.getFileTypeName('')).toBe('未知')
- expect(wrapper.vm.getFileTypeName('unknown/type')).toBe('其他')
- })
-
- it('should return correct tag type for images', () => {
- expect(wrapper.vm.getFileTypeTag('image/png')).toBe('success')
- })
-
- it('should return correct tag type for videos', () => {
- expect(wrapper.vm.getFileTypeTag('video/mp4')).toBe('danger')
- })
-
- it('should return correct tag type for audio', () => {
- expect(wrapper.vm.getFileTypeTag('audio/mp3')).toBe('warning')
- })
-
- it('should return correct tag type for PDF', () => {
- expect(wrapper.vm.getFileTypeTag('application/pdf')).toBe('danger')
- })
-
- it('should return correct tag type for unknown', () => {
- expect(wrapper.vm.getFileTypeTag('')).toBe('info')
- })
- })
-
- describe('search functionality', () => {
- beforeEach(() => {
- wrapper = mount(FileManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-upload': true,
- 'el-tag': true,
- 'el-icon': true,
- },
- },
- })
- })
-
- it('should filter files by search keyword', async () => {
- wrapper.vm.dataSource = [
- { id: 1, fileName: 'test.pdf' },
- { id: 2, fileName: 'image.png' },
- { id: 3, fileName: 'document.doc' },
- ]
-
- wrapper.vm.searchKeyword = 'test'
- await wrapper.vm.$nextTick()
-
- expect(wrapper.vm.filteredDataSource.length).toBe(1)
- expect(wrapper.vm.filteredDataSource[0].fileName).toBe('test.pdf')
- })
-
- it('should return all files when search keyword is empty', () => {
- wrapper.vm.dataSource = [
- { id: 1, fileName: 'test.pdf' },
- { id: 2, fileName: 'image.png' },
- ]
-
- wrapper.vm.searchKeyword = ''
-
- expect(wrapper.vm.filteredDataSource.length).toBe(2)
- })
-
- it('should be case insensitive when searching', () => {
- wrapper.vm.dataSource = [
- { id: 1, fileName: 'TEST.pdf' },
- { id: 2, fileName: 'image.png' },
- ]
-
- wrapper.vm.searchKeyword = 'test'
-
- expect(wrapper.vm.filteredDataSource.length).toBe(1)
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/Login.test.ts b/novalon-manage-web/src/__tests__/components/Login.test.ts
deleted file mode 100644
index 20904bf..0000000
--- a/novalon-manage-web/src/__tests__/components/Login.test.ts
+++ /dev/null
@@ -1,186 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import { createPinia, setActivePinia } from 'pinia'
-import Login from '@/views/system/Login.vue'
-
-vi.mock('vue-router')
-vi.mock('element-plus', () => ({
- ElMessage: {
- success: vi.fn(),
- error: vi.fn(),
- },
-}))
-
-vi.mock('@/utils/request', () => ({
- default: {
- post: vi.fn(),
- },
-}))
-
-describe('Login Component', () => {
- let router: any
- let wrapper: any
- let pinia: any
-
- beforeEach(() => {
- pinia = createPinia()
- setActivePinia(pinia)
-
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Dashboard
' } },
- { path: '/login', component: { template: 'Login
' } },
- ],
- })
-
- vi.clearAllMocks()
- localStorage.clear()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component rendering', () => {
- it('should render login form', () => {
- wrapper = mount(Login, {
- global: {
- plugins: [router, pinia],
- stubs: {
- 'el-card': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-button': true,
- },
- },
- })
-
- expect(wrapper.find('.login-container').exists()).toBe(true)
- expect(wrapper.find('.login-card').exists()).toBe(true)
- })
-
- it('should initialize with empty form state', () => {
- wrapper = mount(Login, {
- global: {
- plugins: [router, pinia],
- stubs: {
- 'el-card': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-button': true,
- },
- },
- })
-
- expect(wrapper.vm.formState.username).toBe('')
- expect(wrapper.vm.formState.password).toBe('')
- })
-
- it('should initialize loading as false', () => {
- wrapper = mount(Login, {
- global: {
- plugins: [router, pinia],
- stubs: {
- 'el-card': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-button': true,
- },
- },
- })
-
- expect(wrapper.vm.loading).toBe(false)
- })
- })
-
- describe('form state management', () => {
- it('should update username when input changes', async () => {
- wrapper = mount(Login, {
- global: {
- plugins: [router, pinia],
- stubs: {
- 'el-card': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-button': true,
- },
- },
- })
-
- wrapper.vm.formState.username = 'testuser'
- await wrapper.vm.$nextTick()
-
- expect(wrapper.vm.formState.username).toBe('testuser')
- })
-
- it('should update password when input changes', async () => {
- wrapper = mount(Login, {
- global: {
- plugins: [router, pinia],
- stubs: {
- 'el-card': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-button': true,
- },
- },
- })
-
- wrapper.vm.formState.password = 'password123'
- await wrapper.vm.$nextTick()
-
- expect(wrapper.vm.formState.password).toBe('password123')
- })
- })
-
- describe('form submission', () => {
- it('should have onFinish method', () => {
- wrapper = mount(Login, {
- global: {
- plugins: [router, pinia],
- stubs: {
- 'el-card': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-button': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.onFinish).toBe('function')
- })
- })
-
- describe('document title', () => {
- it('should set document title on mount', () => {
- const originalTitle = document.title
-
- wrapper = mount(Login, {
- global: {
- plugins: [router, pinia],
- stubs: {
- 'el-card': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-button': true,
- },
- },
- })
-
- expect(document.title).toBe('登录 - Novalon 管理系统')
-
- document.title = originalTitle
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/LoginLog.test.ts b/novalon-manage-web/src/__tests__/components/LoginLog.test.ts
deleted file mode 100644
index 09d6be8..0000000
--- a/novalon-manage-web/src/__tests__/components/LoginLog.test.ts
+++ /dev/null
@@ -1,195 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import LoginLog from '@/views/audit/LoginLog.vue'
-
-vi.mock('vue-router')
-vi.mock('@/utils/request', () => {
- const mockRequest = {
- get: vi.fn().mockResolvedValue({
- content: [
- { id: 1, username: 'admin', ip: '192.168.1.1', location: '北京', browser: 'Chrome', os: 'Windows', status: '0', loginTime: '2026-01-01T10:00:00' },
- { id: 2, username: 'user', ip: '192.168.1.2', location: '上海', browser: 'Firefox', os: 'MacOS', status: '1', loginTime: '2026-01-02T11:00:00' },
- ],
- totalElements: 2,
- }),
- post: vi.fn(),
- put: vi.fn(),
- delete: vi.fn(),
- }
-
- return {
- default: mockRequest,
- }
-})
-
-describe('LoginLog Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render login log container', () => {
- wrapper = mount(LoginLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- },
- },
- })
-
- expect(wrapper.find('.login-log').exists()).toBe(true)
- })
-
- it('should initialize with empty search keyword', () => {
- wrapper = mount(LoginLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- },
- },
- })
-
- expect(wrapper.vm.searchKeyword).toBe('')
- })
-
- it('should initialize with correct pagination defaults', () => {
- wrapper = mount(LoginLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- },
- },
- })
-
- expect(wrapper.vm.pagination.current).toBe(1)
- expect(wrapper.vm.pagination.pageSize).toBe(10)
- expect(wrapper.vm.pagination.total).toBe(0)
- })
-
- it('should initialize with correct sort defaults', () => {
- wrapper = mount(LoginLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- },
- },
- })
-
- expect(wrapper.vm.sortInfo.sort).toBe('id')
- expect(wrapper.vm.sortInfo.order).toBe('asc')
- })
- })
-
- describe('sort handling', () => {
- beforeEach(() => {
- wrapper = mount(LoginLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- },
- },
- })
- })
-
- it('should update sort info on ascending order', () => {
- wrapper.vm.handleSortChange({ prop: 'username', order: 'ascending' })
- expect(wrapper.vm.sortInfo.sort).toBe('username')
- expect(wrapper.vm.sortInfo.order).toBe('asc')
- })
-
- it('should update sort info on descending order', () => {
- wrapper.vm.handleSortChange({ prop: 'loginTime', order: 'descending' })
- expect(wrapper.vm.sortInfo.sort).toBe('loginTime')
- expect(wrapper.vm.sortInfo.order).toBe('desc')
- })
- })
-
- describe('pagination handling', () => {
- beforeEach(() => {
- wrapper = mount(LoginLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-pagination': true,
- },
- },
- })
- })
-
- it('should reset to first page on size change', () => {
- wrapper.vm.pagination.current = 5
- wrapper.vm.handleSizeChange()
- expect(wrapper.vm.pagination.current).toBe(1)
- })
-
- it('should reset to first page on search', () => {
- wrapper.vm.pagination.current = 5
- wrapper.vm.handleSearch()
- expect(wrapper.vm.pagination.current).toBe(1)
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/MenuItem.test.ts b/novalon-manage-web/src/__tests__/components/MenuItem.test.ts
deleted file mode 100644
index 34a3303..0000000
--- a/novalon-manage-web/src/__tests__/components/MenuItem.test.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { describe, it, expect } from 'vitest'
-import { mount } from '@vue/test-utils'
-import MenuItem from '@/components/MenuItem.vue'
-
-describe('MenuItem 组件', () => {
- it('应该正确接收菜单项 props', () => {
- const menu = {
- id: '1',
- name: '仪表盘',
- path: '/dashboard',
- icon: 'Odometer',
- sort: 1
- }
-
- const wrapper = mount(MenuItem, {
- props: { menu },
- global: {
- stubs: {
- 'el-menu-item': {
- template: '
'
- },
- 'el-sub-menu': {
- template: '
'
- },
- 'el-icon': {
- template: '
'
- }
- }
- }
- })
-
- expect(wrapper.props('menu')).toEqual(menu)
- })
-
- it('应该正确处理有子菜单的菜单项', () => {
- const menu = {
- id: '2',
- name: '系统管理',
- path: '/system',
- icon: 'Setting',
- sort: 2,
- children: [
- {
- id: '3',
- name: '用户管理',
- path: '/users',
- sort: 1
- }
- ]
- }
-
- const wrapper = mount(MenuItem, {
- props: { menu },
- global: {
- stubs: {
- 'el-menu-item': {
- template: '
'
- },
- 'el-sub-menu': {
- template: '
'
- },
- 'el-icon': {
- template: '
'
- }
- }
- }
- })
-
- expect(wrapper.props('menu')).toEqual(menu)
- expect(wrapper.props('menu').children).toHaveLength(1)
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/MenuManagement.test.ts b/novalon-manage-web/src/__tests__/components/MenuManagement.test.ts
deleted file mode 100644
index 7e92ff1..0000000
--- a/novalon-manage-web/src/__tests__/components/MenuManagement.test.ts
+++ /dev/null
@@ -1,279 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import MenuManagement from '@/views/system/MenuManagement.vue'
-
-vi.mock('vue-router')
-vi.mock('element-plus', () => ({
- ElMessage: {
- success: vi.fn(),
- error: vi.fn(),
- },
- ElMessageBox: {
- confirm: vi.fn(),
- },
-}))
-
-vi.mock('@/api/menu.api', () => ({
- menuApi: {
- getAll: vi.fn(),
- create: vi.fn(),
- update: vi.fn(),
- delete: vi.fn(),
- },
-}))
-
-vi.mock('@/utils/request', () => {
- const mockRequest = {
- get: vi.fn(),
- post: vi.fn(),
- put: vi.fn(),
- delete: vi.fn(),
- }
-
- mockRequest.get.mockResolvedValue([])
- mockRequest.post.mockResolvedValue({})
- mockRequest.put.mockResolvedValue({})
- mockRequest.delete.mockResolvedValue({})
-
- return {
- default: mockRequest,
- }
-})
-
-describe('MenuManagement Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render menu management container', () => {
- wrapper = mount(MenuManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-input-number': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.find('.menu-management').exists()).toBe(true)
- })
-
- it('should initialize with empty data source', () => {
- wrapper = mount(MenuManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-input-number': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.dataSource).toBeDefined()
- expect(Array.isArray(wrapper.vm.dataSource)).toBe(true)
- })
-
- it('should initialize with loading state false', () => {
- wrapper = mount(MenuManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-input-number': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.loading).toBeDefined()
- expect(typeof wrapper.vm.loading).toBe('boolean')
- })
-
- it('should initialize with modal visible false', () => {
- wrapper = mount(MenuManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-input-number': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.modalVisible).toBe(false)
- })
-
- it('should initialize with empty form state', () => {
- wrapper = mount(MenuManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-input-number': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.formState.menuName).toBe('')
- expect(wrapper.vm.formState.menuType).toBe('C')
- expect(wrapper.vm.formState.perms).toBe('')
- expect(wrapper.vm.formState.component).toBe('')
- expect(wrapper.vm.formState.orderNum).toBe(0)
- expect(wrapper.vm.formState.status).toBe('0')
- })
- })
-
- describe('add menu functionality', () => {
- it('should have handleAdd method', () => {
- wrapper = mount(MenuManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-input-number': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleAdd).toBe('function')
- })
- })
-
- describe('edit menu functionality', () => {
- it('should have handleEdit method', () => {
- wrapper = mount(MenuManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-input-number': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleEdit).toBe('function')
- })
- })
-
- describe('delete menu functionality', () => {
- it('should have handleDelete method', () => {
- wrapper = mount(MenuManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-input': true,
- 'el-input-number': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleDelete).toBe('function')
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/NoticeManagement.test.ts b/novalon-manage-web/src/__tests__/components/NoticeManagement.test.ts
deleted file mode 100644
index 0984d27..0000000
--- a/novalon-manage-web/src/__tests__/components/NoticeManagement.test.ts
+++ /dev/null
@@ -1,231 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import NoticeManagement from '@/views/notify/NoticeManagement.vue'
-
-vi.mock('vue-router')
-vi.mock('element-plus', () => ({
- ElMessage: {
- success: vi.fn(),
- error: vi.fn(),
- },
- ElMessageBox: {
- confirm: vi.fn(),
- },
-}))
-
-vi.mock('@/utils/request', () => {
- const mockRequest = {
- get: vi.fn().mockResolvedValue([
- { id: 1, noticeTitle: '系统维护通知', noticeType: '1', noticeContent: '系统将于今晚维护', status: '0', createdAt: '2026-01-01T10:00:00' },
- { id: 2, noticeTitle: '新功能上线', noticeType: '2', noticeContent: '新功能已上线', status: '0', createdAt: '2026-01-02T11:00:00' },
- ]),
- post: vi.fn().mockResolvedValue({}),
- put: vi.fn().mockResolvedValue({}),
- delete: vi.fn().mockResolvedValue({}),
- }
-
- return {
- default: mockRequest,
- }
-})
-
-describe('NoticeManagement Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render notice management container', () => {
- wrapper = mount(NoticeManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- },
- },
- })
-
- expect(wrapper.find('.notice-management').exists()).toBe(true)
- })
-
- it('should initialize with hidden modal', () => {
- wrapper = mount(NoticeManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- },
- },
- })
-
- expect(wrapper.vm.modalVisible).toBe(false)
- })
-
- it('should initialize with empty form state', () => {
- wrapper = mount(NoticeManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- },
- },
- })
-
- expect(wrapper.vm.formState.noticeTitle).toBe('')
- expect(wrapper.vm.formState.noticeType).toBe('1')
- expect(wrapper.vm.formState.status).toBe('0')
- })
- })
-
- describe('add notice', () => {
- beforeEach(() => {
- wrapper = mount(NoticeManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- },
- },
- })
- })
-
- it('should show modal with add title', () => {
- wrapper.vm.handleAdd()
- expect(wrapper.vm.modalTitle).toBe('新增公告')
- expect(wrapper.vm.modalVisible).toBe(true)
- })
-
- it('should reset form state when adding', () => {
- wrapper.vm.formState.noticeTitle = 'existing title'
- wrapper.vm.handleAdd()
- expect(wrapper.vm.formState.noticeTitle).toBe('')
- expect(wrapper.vm.formState.id).toBe(null)
- })
- })
-
- describe('edit notice', () => {
- beforeEach(() => {
- wrapper = mount(NoticeManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- },
- },
- })
- })
-
- it('should show modal with edit title', () => {
- const notice = { id: 1, noticeTitle: 'Test', noticeType: '1', noticeContent: 'Content', status: '0' }
- wrapper.vm.handleEdit(notice)
- expect(wrapper.vm.modalTitle).toBe('编辑公告')
- expect(wrapper.vm.modalVisible).toBe(true)
- })
-
- it('should populate form with notice data', () => {
- const notice = { id: 1, noticeTitle: 'Test Notice', noticeType: '2', noticeContent: 'Test Content', status: '1' }
- wrapper.vm.handleEdit(notice)
- expect(wrapper.vm.formState.id).toBe(1)
- expect(wrapper.vm.formState.noticeTitle).toBe('Test Notice')
- expect(wrapper.vm.formState.noticeType).toBe('2')
- })
- })
-
- describe('form state', () => {
- beforeEach(() => {
- wrapper = mount(NoticeManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- },
- },
- })
- })
-
- it('should have default notice type as notification', () => {
- expect(wrapper.vm.formState.noticeType).toBe('1')
- })
-
- it('should have default status as normal', () => {
- expect(wrapper.vm.formState.status).toBe('0')
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/OperationLog.test.ts b/novalon-manage-web/src/__tests__/components/OperationLog.test.ts
deleted file mode 100644
index 3e18b4a..0000000
--- a/novalon-manage-web/src/__tests__/components/OperationLog.test.ts
+++ /dev/null
@@ -1,216 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import OperationLog from '@/views/audit/OperationLog.vue'
-
-vi.mock('vue-router')
-vi.mock('@/api/operationLog', () => ({
- operationLogApi: {
- getPage: vi.fn().mockResolvedValue({
- content: [
- { id: 1, username: 'admin', operation: '用户登录', method: 'POST', params: '{}', status: '0', duration: 100, createdAt: '2026-01-01T10:00:00' },
- { id: 2, username: 'user', operation: '查看用户', method: 'GET', params: '{"id":1}', status: '0', duration: 50, createdAt: '2026-01-02T11:00:00' },
- ],
- totalElements: 2,
- }),
- },
-}))
-
-describe('OperationLog Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render operation log container', () => {
- wrapper = mount(OperationLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-popover': true,
- 'el-pagination': true,
- },
- },
- })
-
- expect(wrapper.find('.operation-log').exists()).toBe(true)
- })
-
- it('should initialize with empty search keyword', () => {
- wrapper = mount(OperationLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-popover': true,
- 'el-pagination': true,
- },
- },
- })
-
- expect(wrapper.vm.searchKeyword).toBe('')
- })
-
- it('should initialize with correct pagination defaults', () => {
- wrapper = mount(OperationLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-popover': true,
- 'el-pagination': true,
- },
- },
- })
-
- expect(wrapper.vm.pagination.current).toBe(1)
- expect(wrapper.vm.pagination.pageSize).toBe(10)
- expect(wrapper.vm.pagination.total).toBe(0)
- })
- })
-
- describe('operation icon mapping', () => {
- beforeEach(() => {
- wrapper = mount(OperationLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-popover': true,
- 'el-pagination': true,
- },
- },
- })
- })
-
- it('should return User icon for login operations', () => {
- const icon = wrapper.vm.getOperationIcon('用户登录')
- expect(icon.name).toBe('User')
- })
-
- it('should return Delete icon for delete operations', () => {
- const icon = wrapper.vm.getOperationIcon('删除用户')
- expect(icon.name).toBe('Delete')
- })
-
- it('should return Edit icon for update operations', () => {
- const icon = wrapper.vm.getOperationIcon('编辑用户')
- expect(icon.name).toBe('Edit')
- })
-
- it('should return View icon for view operations', () => {
- const icon = wrapper.vm.getOperationIcon('查看用户')
- expect(icon.name).toBe('View')
- })
-
- it('should return Plus icon for create operations', () => {
- const icon = wrapper.vm.getOperationIcon('新增用户')
- expect(icon.name).toBe('Plus')
- })
-
- it('should return Download icon for download operations', () => {
- const icon = wrapper.vm.getOperationIcon('下载文件')
- expect(icon.name).toBe('Download')
- })
-
- it('should return Setting icon for config operations', () => {
- const icon = wrapper.vm.getOperationIcon('系统设置')
- expect(icon.name).toBe('Setting')
- })
-
- it('should return Lock icon for password operations', () => {
- const icon = wrapper.vm.getOperationIcon('重置密码')
- expect(icon.name).toBe('Lock')
- })
-
- it('should return Document icon for unknown operations', () => {
- const icon = wrapper.vm.getOperationIcon('未知操作')
- expect(icon.name).toBe('Document')
- })
- })
-
- describe('params formatting', () => {
- beforeEach(() => {
- wrapper = mount(OperationLog, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-input': true,
- 'el-tag': true,
- 'el-icon': true,
- 'el-popover': true,
- 'el-pagination': true,
- },
- },
- })
- })
-
- it('should format valid JSON params', () => {
- const params = '{"name":"test","id":1}'
- const formatted = wrapper.vm.formatParams(params)
- expect(formatted).toContain('name')
- expect(formatted).toContain('test')
- })
-
- it('should return empty string for null params', () => {
- const formatted = wrapper.vm.formatParams(null)
- expect(formatted).toBe('')
- })
-
- it('should return empty string for undefined params', () => {
- const formatted = wrapper.vm.formatParams(undefined)
- expect(formatted).toBe('')
- })
-
- it('should return original string for invalid JSON', () => {
- const params = 'not a json'
- const formatted = wrapper.vm.formatParams(params)
- expect(formatted).toBe('not a json')
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/RoleManagement.test.ts b/novalon-manage-web/src/__tests__/components/RoleManagement.test.ts
deleted file mode 100644
index a9787ef..0000000
--- a/novalon-manage-web/src/__tests__/components/RoleManagement.test.ts
+++ /dev/null
@@ -1,383 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import RoleManagement from '@/views/system/RoleManagement.vue'
-
-vi.mock('vue-router')
-vi.mock('element-plus', () => ({
- ElMessage: {
- success: vi.fn(),
- error: vi.fn(),
- },
- ElMessageBox: {
- confirm: vi.fn(),
- },
-}))
-
-vi.mock('@/api/role.api', () => ({
- roleApi: {
- getPage: vi.fn(),
- create: vi.fn(),
- update: vi.fn(),
- delete: vi.fn(),
- getAll: vi.fn(),
- },
-}))
-
-vi.mock('@/api/permission.api', () => ({
- permissionApi: {
- getAll: vi.fn(),
- },
-}))
-
-describe('RoleManagement Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render role management container', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.find('.role-management').exists()).toBe(true)
- })
-
- it('should initialize with empty search keyword', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.searchKeyword).toBe('')
- })
-
- it('should initialize with empty data source', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.dataSource).toEqual([])
- })
-
- it('should initialize with pagination on page 1', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.pagination.current).toBe(1)
- })
-
- it('should initialize with modal visible false', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.modalVisible).toBe(false)
- })
-
- it('should initialize with empty form state', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.formState.roleName).toBe('')
- expect(wrapper.vm.formState.roleKey).toBe('')
- expect(wrapper.vm.formState.roleSort).toBe(1)
- expect(wrapper.vm.formState.status).toBe(1)
- expect(wrapper.vm.formState.permissions).toEqual([])
- })
- })
-
- describe('search functionality', () => {
- it('should have handleSearch method', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleSearch).toBe('function')
- })
-
- it('should update search keyword when input changes', async () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- wrapper.vm.searchKeyword = 'admin'
- await wrapper.vm.$nextTick()
-
- expect(wrapper.vm.searchKeyword).toBe('admin')
- })
- })
-
- describe('add role functionality', () => {
- it('should have handleAdd method', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleAdd).toBe('function')
- })
- })
-
- describe('pagination functionality', () => {
- it('should have handleTableChange method', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleTableChange).toBe('function')
- })
-
- it('should have handleSizeChange method', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleSizeChange).toBe('function')
- })
- })
-
- describe('sort functionality', () => {
- it('should have handleSortChange method', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleSortChange).toBe('function')
- })
-
- it('should initialize with default sort info', () => {
- wrapper = mount(RoleManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-tree': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.sortInfo.sortBy).toBe('id')
- expect(wrapper.vm.sortInfo.sortOrder).toBe('asc')
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/components/UserManagement.test.ts b/novalon-manage-web/src/__tests__/components/UserManagement.test.ts
deleted file mode 100644
index edaef04..0000000
--- a/novalon-manage-web/src/__tests__/components/UserManagement.test.ts
+++ /dev/null
@@ -1,423 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createRouter, createMemoryHistory } from 'vue-router'
-import UserManagement from '@/views/system/UserManagement.vue'
-
-vi.mock('vue-router')
-vi.mock('element-plus', () => ({
- ElMessage: {
- success: vi.fn(),
- error: vi.fn(),
- },
- ElMessageBox: {
- confirm: vi.fn(),
- },
-}))
-
-vi.mock('@/api/user.api', () => ({
- userApi: {
- getPage: vi.fn(),
- create: vi.fn(),
- update: vi.fn(),
- delete: vi.fn(),
- assignRoles: vi.fn(),
- },
-}))
-
-vi.mock('@/api/role.api', () => ({
- roleApi: {
- getAll: vi.fn(),
- },
-}))
-
-describe('UserManagement Component', () => {
- let router: any
- let wrapper: any
-
- beforeEach(() => {
- router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: '/', component: { template: 'Home
' } },
- ],
- })
-
- vi.clearAllMocks()
- })
-
- afterEach(() => {
- if (wrapper) {
- wrapper.unmount()
- }
- })
-
- describe('component initialization', () => {
- it('should render user management container', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.find('.user-management').exists()).toBe(true)
- })
-
- it('should initialize with empty search keyword', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.searchKeyword).toBe('')
- })
-
- it('should initialize with loading state false before data fetch', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.loading).toBeDefined()
- expect(typeof wrapper.vm.loading).toBe('boolean')
- })
-
- it('should initialize with empty data source', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.dataSource).toEqual([])
- })
-
- it('should initialize with pagination on page 1', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.pagination.current).toBe(1)
- })
-
- it('should initialize with modal visible false', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.modalVisible).toBe(false)
- })
-
- it('should initialize with empty form state', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.formState.username).toBe('')
- expect(wrapper.vm.formState.password).toBe('')
- expect(wrapper.vm.formState.nickname).toBe('')
- expect(wrapper.vm.formState.email).toBe('')
- expect(wrapper.vm.formState.phone).toBe('')
- expect(wrapper.vm.formState.roles).toEqual([])
- })
- })
-
- describe('search functionality', () => {
- it('should have handleSearch method', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleSearch).toBe('function')
- })
-
- it('should update search keyword when input changes', async () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- wrapper.vm.searchKeyword = 'testuser'
- await wrapper.vm.$nextTick()
-
- expect(wrapper.vm.searchKeyword).toBe('testuser')
- })
- })
-
- describe('add user functionality', () => {
- it('should have handleAdd method', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleAdd).toBe('function')
- })
- })
-
- describe('pagination functionality', () => {
- it('should have handleTableChange method', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleTableChange).toBe('function')
- })
-
- it('should have handleSizeChange method', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleSizeChange).toBe('function')
- })
- })
-
- describe('sort functionality', () => {
- it('should have handleSortChange method', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(typeof wrapper.vm.handleSortChange).toBe('function')
- })
-
- it('should initialize with default sort info', () => {
- wrapper = mount(UserManagement, {
- global: {
- plugins: [router],
- stubs: {
- 'el-card': true,
- 'el-input': true,
- 'el-button': true,
- 'el-table': true,
- 'el-table-column': true,
- 'el-tag': true,
- 'el-pagination': true,
- 'el-dialog': true,
- 'el-form': true,
- 'el-form-item': true,
- 'el-select': true,
- 'el-option': true,
- 'el-icon': true,
- },
- },
- })
-
- expect(wrapper.vm.sortInfo.sortBy).toBe('id')
- expect(wrapper.vm.sortInfo.sortOrder).toBe('asc')
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/config.test.ts b/novalon-manage-web/src/__tests__/config.test.ts
deleted file mode 100644
index 59d1009..0000000
--- a/novalon-manage-web/src/__tests__/config.test.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { describe, it, expect } from 'vitest'
-
-describe('Vitest Configuration Test', () => {
- it('should run a simple test', () => {
- expect(1 + 1).toBe(2)
- })
-
- it('should handle async operations', async () => {
- const result = await Promise.resolve(42)
- expect(result).toBe(42)
- })
-})
diff --git a/novalon-manage-web/src/__tests__/directives/permission.test.ts b/novalon-manage-web/src/__tests__/directives/permission.test.ts
deleted file mode 100644
index 9dfc020..0000000
--- a/novalon-manage-web/src/__tests__/directives/permission.test.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-import { describe, it, expect, beforeEach } from 'vitest'
-import { mount } from '@vue/test-utils'
-import { createPinia, setActivePinia } from 'pinia'
-import { permissionDirective } from '@/directives/permission'
-import { usePermissionStore } from '@/stores/permission'
-
-describe('v-permission 指令', () => {
- beforeEach(() => {
- setActivePinia(createPinia())
- localStorage.clear()
- })
-
- describe('角色检查', () => {
- it('有角色时应该显示元素', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: ['admin'],
- permissions: [],
- menus: []
- })
-
- const wrapper = mount({
- template: '',
- directives: {
- permission: permissionDirective
- }
- })
-
- expect(wrapper.find('button').isVisible()).toBe(true)
- })
-
- it('无角色时应该隐藏元素', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: ['user'],
- permissions: [],
- menus: []
- })
-
- const wrapper = mount({
- template: '',
- directives: {
- permission: permissionDirective
- }
- })
-
- expect(wrapper.find('button').isVisible()).toBe(false)
- })
-
- it('支持数组参数(满足任一即可)', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: ['user'],
- permissions: [],
- menus: []
- })
-
- const wrapper = mount({
- template: '',
- directives: {
- permission: permissionDirective
- }
- })
-
- expect(wrapper.find('button').isVisible()).toBe(true)
- })
- })
-
- describe('权限检查', () => {
- it('有权限时应该显示元素', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: [],
- permissions: ['user:delete'],
- menus: []
- })
-
- const wrapper = mount({
- template: '',
- directives: {
- permission: permissionDirective
- }
- })
-
- expect(wrapper.find('button').isVisible()).toBe(true)
- })
-
- it('无权限时应该隐藏元素', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: [],
- permissions: ['user:read'],
- menus: []
- })
-
- const wrapper = mount({
- template: '',
- directives: {
- permission: permissionDirective
- }
- })
-
- expect(wrapper.find('button').isVisible()).toBe(false)
- })
-
- it('支持简写形式(默认权限检查)', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: [],
- permissions: ['user:create'],
- menus: []
- })
-
- const wrapper = mount({
- template: '',
- directives: {
- permission: permissionDirective
- }
- })
-
- expect(wrapper.find('button').isVisible()).toBe(true)
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/fixtures.ts b/novalon-manage-web/src/__tests__/fixtures.ts
deleted file mode 100644
index 8265710..0000000
--- a/novalon-manage-web/src/__tests__/fixtures.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-export const mockUser = {
- id: 1,
- username: 'testuser',
- nickname: 'Test User',
- email: 'test@example.com',
- phone: '13800138000',
- avatar: 'https://example.com/avatar.jpg',
- roles: ['admin'],
- permissions: ['user:view', 'user:create', 'user:edit', 'user:delete'],
-}
-
-export const mockRole = {
- id: 1,
- roleName: '测试角色',
- roleKey: 'test_role',
- roleSort: 1,
- status: '1',
- remark: '测试角色备注',
- createTime: new Date().toISOString(),
- updateTime: new Date().toISOString(),
-}
-
-export const mockMenu = {
- id: 1,
- menuName: '系统管理',
- parentId: 0,
- orderNum: 1,
- menuType: 'M',
- component: 'system',
- perms: 'system:view',
- status: '1',
- createTime: new Date().toISOString(),
- updateTime: new Date().toISOString(),
-}
-
-export const mockDict = {
- id: 1,
- dictName: '用户状态',
- dictType: 'user_status',
- status: '1',
- remark: '用户状态字典',
- createTime: new Date().toISOString(),
- updateTime: new Date().toISOString(),
-}
-
-export const mockConfig = {
- id: 1,
- configName: '系统名称',
- configKey: 'sys.name',
- configValue: 'Novalon管理系统',
- configType: 'Y',
- status: '1',
- remark: '系统名称配置',
- createTime: new Date().toISOString(),
- updateTime: new Date().toISOString(),
-}
-
-export const mockNotice = {
- id: 1,
- noticeTitle: '系统通知',
- noticeType: '1',
- noticeContent: '这是一条测试通知',
- status: '0',
- createTime: new Date().toISOString(),
- updateTime: new Date().toISOString(),
-}
-
-export const mockLoginRequest = {
- username: 'admin',
- password: 'admin123',
-}
-
-export const mockLoginResponse = {
- token: 'mock-jwt-token',
- user: mockUser,
-}
-
-export const mockApiResponse = (data: T, code = 200, message = 'success') => ({
- code,
- message,
- data,
-})
-
-export const mockErrorResponse = (code = 500, message = 'Internal Server Error') => ({
- code,
- message,
- data: null,
-})
diff --git a/novalon-manage-web/src/__tests__/router/permission.guard.test.ts b/novalon-manage-web/src/__tests__/router/permission.guard.test.ts
deleted file mode 100644
index a307bcd..0000000
--- a/novalon-manage-web/src/__tests__/router/permission.guard.test.ts
+++ /dev/null
@@ -1,291 +0,0 @@
-import { describe, it, expect, beforeEach } from 'vitest'
-import { createRouter, createWebHistory } from 'vue-router'
-import type { RouteRecordRaw } from 'vue-router'
-
-const mockLocalStorage = {
- store: {} as Record,
- getItem(key: string) {
- return this.store[key] || null
- },
- setItem(key: string, value: string) {
- this.store[key] = value
- },
- removeItem(key: string) {
- delete this.store[key]
- },
- clear() {
- this.store = {}
- }
-}
-
-Object.defineProperty(window, 'localStorage', {
- value: mockLocalStorage
-})
-
-const createTestRouter = (routes: RouteRecordRaw[]) => {
- return createRouter({
- history: createWebHistory(),
- routes
- })
-}
-
-describe('路由守卫权限检查', () => {
- beforeEach(() => {
- mockLocalStorage.clear()
- })
-
- describe('基础认证检查', () => {
- it('未登录用户访问受保护路由应重定向到登录页', async () => {
- const routes: RouteRecordRaw[] = [
- {
- path: '/login',
- name: 'Login',
- component: { template: 'Login
' }
- },
- {
- path: '/',
- component: { template: 'Layout
' },
- meta: { requiresAuth: true },
- children: [
- {
- path: 'dashboard',
- name: 'Dashboard',
- component: { template: 'Dashboard
' }
- }
- ]
- }
- ]
-
- const router = createTestRouter(routes)
-
- router.beforeEach((to, _from, next) => {
- const token = localStorage.getItem('token')
-
- if (to.meta.requiresAuth && !token) {
- next('/login')
- } else {
- next()
- }
- })
-
- await router.push('/dashboard')
- expect(router.currentRoute.value.path).toBe('/login')
- })
-
- it('已登录用户访问受保护路由应允许通过', async () => {
- mockLocalStorage.setItem('token', 'valid-token')
-
- const routes: RouteRecordRaw[] = [
- {
- path: '/login',
- name: 'Login',
- component: { template: 'Login
' }
- },
- {
- path: '/',
- component: { template: 'Layout
' },
- meta: { requiresAuth: true },
- children: [
- {
- path: 'dashboard',
- name: 'Dashboard',
- component: { template: 'Dashboard
' }
- }
- ]
- }
- ]
-
- const router = createTestRouter(routes)
-
- router.beforeEach((to, _from, next) => {
- const token = localStorage.getItem('token')
-
- if (to.meta.requiresAuth && !token) {
- next('/login')
- } else {
- next()
- }
- })
-
- await router.push('/dashboard')
- expect(router.currentRoute.value.path).toBe('/dashboard')
- })
- })
-
- describe('角色权限检查', () => {
- it('普通用户访问管理员路由应重定向到403页面', async () => {
- mockLocalStorage.setItem('token', 'valid-token')
- mockLocalStorage.setItem('roles', JSON.stringify(['user']))
-
- const routes: RouteRecordRaw[] = [
- {
- path: '/login',
- name: 'Login',
- component: { template: 'Login
' }
- },
- {
- path: '/403',
- name: 'Forbidden',
- component: { template: '403 Forbidden
' }
- },
- {
- path: '/',
- component: { template: 'Layout
' },
- meta: { requiresAuth: true },
- children: [
- {
- path: 'dashboard',
- name: 'Dashboard',
- component: { template: 'Dashboard
' }
- },
- {
- path: 'users',
- name: 'UserManagement',
- component: { template: 'UserManagement
' },
- meta: { roles: ['admin'] }
- }
- ]
- }
- ]
-
- const router = createTestRouter(routes)
-
- router.beforeEach((to, _from, next) => {
- const token = localStorage.getItem('token')
- const rolesStr = localStorage.getItem('roles')
- const userRoles = rolesStr ? JSON.parse(rolesStr) : []
-
- if (to.meta.requiresAuth && !token) {
- next('/login')
- return
- }
-
- if (to.meta.roles && Array.isArray(to.meta.roles)) {
- const hasRole = to.meta.roles.some((role: string) => userRoles.includes(role))
- if (!hasRole) {
- next('/403')
- return
- }
- }
-
- next()
- })
-
- await router.push('/users')
- expect(router.currentRoute.value.path).toBe('/403')
- })
-
- it('管理员用户访问管理员路由应允许通过', async () => {
- mockLocalStorage.setItem('token', 'valid-token')
- mockLocalStorage.setItem('roles', JSON.stringify(['admin']))
-
- const routes: RouteRecordRaw[] = [
- {
- path: '/login',
- name: 'Login',
- component: { template: 'Login
' }
- },
- {
- path: '/403',
- name: 'Forbidden',
- component: { template: '403 Forbidden
' }
- },
- {
- path: '/',
- component: { template: 'Layout
' },
- meta: { requiresAuth: true },
- children: [
- {
- path: 'dashboard',
- name: 'Dashboard',
- component: { template: 'Dashboard
' }
- },
- {
- path: 'users',
- name: 'UserManagement',
- component: { template: 'UserManagement
' },
- meta: { roles: ['admin'] }
- }
- ]
- }
- ]
-
- const router = createTestRouter(routes)
-
- router.beforeEach((to, _from, next) => {
- const token = localStorage.getItem('token')
- const rolesStr = localStorage.getItem('roles')
- const userRoles = rolesStr ? JSON.parse(rolesStr) : []
-
- if (to.meta.requiresAuth && !token) {
- next('/login')
- return
- }
-
- if (to.meta.roles && Array.isArray(to.meta.roles)) {
- const hasRole = to.meta.roles.some((role: string) => userRoles.includes(role))
- if (!hasRole) {
- next('/403')
- return
- }
- }
-
- next()
- })
-
- await router.push('/users')
- expect(router.currentRoute.value.path).toBe('/users')
- })
-
- it('无角色要求的路由所有登录用户都可访问', async () => {
- mockLocalStorage.setItem('token', 'valid-token')
- mockLocalStorage.setItem('roles', JSON.stringify(['user']))
-
- const routes: RouteRecordRaw[] = [
- {
- path: '/login',
- name: 'Login',
- component: { template: 'Login
' }
- },
- {
- path: '/',
- component: { template: 'Layout
' },
- meta: { requiresAuth: true },
- children: [
- {
- path: 'dashboard',
- name: 'Dashboard',
- component: { template: 'Dashboard
' }
- }
- ]
- }
- ]
-
- const router = createTestRouter(routes)
-
- router.beforeEach((to, _from, next) => {
- const token = localStorage.getItem('token')
- const rolesStr = localStorage.getItem('roles')
- const userRoles = rolesStr ? JSON.parse(rolesStr) : []
-
- if (to.meta.requiresAuth && !token) {
- next('/login')
- return
- }
-
- if (to.meta.roles && Array.isArray(to.meta.roles)) {
- const hasRole = to.meta.roles.some((role: string) => userRoles.includes(role))
- if (!hasRole) {
- next('/403')
- return
- }
- }
-
- next()
- })
-
- await router.push('/dashboard')
- expect(router.currentRoute.value.path).toBe('/dashboard')
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/setup.ts b/novalon-manage-web/src/__tests__/setup.ts
deleted file mode 100644
index acb4577..0000000
--- a/novalon-manage-web/src/__tests__/setup.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { vi } from 'vitest'
-import { config } from '@vue/test-utils'
-
-config.global.stubs = {
- transition: false,
- 'transition-group': false,
-}
-
-Object.defineProperty(window, 'matchMedia', {
- writable: true,
- value: vi.fn().mockImplementation((query) => ({
- matches: false,
- media: query,
- onchange: null,
- addListener: vi.fn(),
- removeListener: vi.fn(),
- addEventListener: vi.fn(),
- removeEventListener: vi.fn(),
- dispatchEvent: vi.fn(),
- })),
-})
-
-const localStorageMock = (() => {
- let store: Record = {}
- return {
- getItem: vi.fn((key: string) => store[key] || null),
- setItem: vi.fn((key: string, value: string) => {
- store[key] = value
- }),
- removeItem: vi.fn((key: string) => {
- delete store[key]
- }),
- clear: vi.fn(() => {
- store = {}
- }),
- }
-})()
-
-Object.defineProperty(window, 'localStorage', {
- value: localStorageMock,
-})
-
-const sessionStorageMock = (() => {
- let store: Record = {}
- return {
- getItem: vi.fn((key: string) => store[key] || null),
- setItem: vi.fn((key: string, value: string) => {
- store[key] = value
- }),
- removeItem: vi.fn((key: string) => {
- delete store[key]
- }),
- clear: vi.fn(() => {
- store = {}
- }),
- }
-})()
-
-Object.defineProperty(window, 'sessionStorage', {
- value: sessionStorageMock,
-})
diff --git a/novalon-manage-web/src/__tests__/stores/permission.test.ts b/novalon-manage-web/src/__tests__/stores/permission.test.ts
deleted file mode 100644
index ac4f0f5..0000000
--- a/novalon-manage-web/src/__tests__/stores/permission.test.ts
+++ /dev/null
@@ -1,167 +0,0 @@
-import { describe, it, expect, beforeEach } from 'vitest'
-import { setActivePinia, createPinia } from 'pinia'
-import { usePermissionStore } from '@/stores/permission'
-
-describe('Permission Store', () => {
- beforeEach(() => {
- setActivePinia(createPinia())
- localStorage.clear()
- })
-
- describe('基础功能', () => {
- it('应该正确初始化状态', () => {
- const store = usePermissionStore()
-
- expect(store.roles).toEqual([])
- expect(store.permissions).toEqual([])
- expect(store.menus).toEqual([])
- expect(store.loaded).toBe(false)
- })
-
- it('应该正确设置权限数据', () => {
- const store = usePermissionStore()
-
- store.setPermissionData({
- roles: ['admin'],
- permissions: ['user:read', 'user:delete'],
- menus: [
- {
- id: '1',
- name: '仪表盘',
- path: '/dashboard',
- icon: 'Odometer',
- sort: 1
- }
- ]
- })
-
- expect(store.roles).toEqual(['admin'])
- expect(store.permissions).toEqual(['user:read', 'user:delete'])
- expect(store.menus).toHaveLength(1)
- expect(store.loaded).toBe(true)
- })
-
- it('应该正确清除权限数据', () => {
- const store = usePermissionStore()
-
- store.setPermissionData({
- roles: ['admin'],
- permissions: ['user:read'],
- menus: []
- })
-
- store.clearPermissionData()
-
- expect(store.roles).toEqual([])
- expect(store.permissions).toEqual([])
- expect(store.menus).toEqual([])
- expect(store.loaded).toBe(false)
- })
- })
-
- describe('权限检查方法', () => {
- it('应该正确检查单个角色', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: ['admin', 'user'],
- permissions: [],
- menus: []
- })
-
- expect(store.hasRole('admin')).toBe(true)
- expect(store.hasRole('manager')).toBe(false)
- })
-
- it('应该正确检查多个角色(满足任一即可)', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: ['user'],
- permissions: [],
- menus: []
- })
-
- expect(store.hasRole(['admin', 'user'])).toBe(true)
- expect(store.hasRole(['admin', 'manager'])).toBe(false)
- })
-
- it('应该正确检查单个权限', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: [],
- permissions: ['user:read', 'user:delete'],
- menus: []
- })
-
- expect(store.hasPermission('user:read')).toBe(true)
- expect(store.hasPermission('user:create')).toBe(false)
- })
-
- it('应该正确检查多个权限(满足任一即可)', () => {
- const store = usePermissionStore()
- store.setPermissionData({
- roles: [],
- permissions: ['user:read'],
- menus: []
- })
-
- expect(store.hasPermission(['user:read', 'user:create'])).toBe(true)
- expect(store.hasPermission(['user:create', 'user:update'])).toBe(false)
- })
- })
-
- describe('localStorage 持久化', () => {
- it('应该正确保存到 localStorage', () => {
- const store = usePermissionStore()
-
- store.setPermissionData({
- roles: ['admin'],
- permissions: ['user:read'],
- menus: [
- {
- id: '1',
- name: '仪表盘',
- path: '/dashboard',
- sort: 1
- }
- ]
- })
-
- const stored = localStorage.getItem('permission')
- expect(stored).toBeTruthy()
-
- const data = JSON.parse(stored!)
- expect(data.roles).toEqual(['admin'])
- expect(data.permissions).toEqual(['user:read'])
- expect(data.menus).toHaveLength(1)
- })
-
- it('应该正确从 localStorage 恢复', () => {
- localStorage.setItem('permission', JSON.stringify({
- roles: ['user'],
- permissions: ['user:read:self'],
- menus: []
- }))
-
- const store = usePermissionStore()
- store.initFromStorage()
-
- expect(store.roles).toEqual(['user'])
- expect(store.permissions).toEqual(['user:read:self'])
- expect(store.loaded).toBe(true)
- })
-
- it('清除数据时应该同时清除 localStorage', () => {
- const store = usePermissionStore()
-
- store.setPermissionData({
- roles: ['admin'],
- permissions: [],
- menus: []
- })
-
- store.clearPermissionData()
-
- expect(localStorage.getItem('permission')).toBeNull()
- })
- })
-})
diff --git a/novalon-manage-web/src/__tests__/utils.ts b/novalon-manage-web/src/__tests__/utils.ts
deleted file mode 100644
index 74aab99..0000000
--- a/novalon-manage-web/src/__tests__/utils.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { VueWrapper } from '@vue/test-utils'
-import { ComponentPublicInstance } from 'vue'
-
-export interface TestHelpers {
- findByText: (text: string) => HTMLElement | null
- findByTestId: (testId: string) => HTMLElement | null
- clickByText: (text: string) => Promise
- clickByTestId: (testId: string) => Promise
- fillByTestId: (testId: string, value: string) => Promise
-}
-
-export function createTestHelpers(wrapper: VueWrapper): TestHelpers {
- return {
- findByText: (text: string) => {
- return wrapper.element.textContent?.includes(text) ? wrapper.element : null
- },
- findByTestId: (testId: string) => {
- return wrapper.element.querySelector(`[data-testid="${testId}"]`)
- },
- clickByText: async (text: string) => {
- const element = wrapper.element.textContent?.includes(text) ? wrapper.element : null
- if (element) {
- element.click()
- await wrapper.vm.$nextTick()
- }
- },
- clickByTestId: async (testId: string) => {
- const element = wrapper.element.querySelector(`[data-testid="${testId}"]`)
- if (element) {
- element.click()
- await wrapper.vm.$nextTick()
- }
- },
- fillByTestId: async (testId: string, value: string) => {
- const element = wrapper.element.querySelector(`[data-testid="${testId}"]`) as HTMLInputElement
- if (element) {
- element.value = value
- element.dispatchEvent(new Event('input', { bubbles: true }))
- await wrapper.vm.$nextTick()
- }
- },
- }
-}
-
-export function waitFor(condition: () => boolean, timeout = 5000): Promise {
- return new Promise((resolve, reject) => {
- const startTime = Date.now()
-
- const check = () => {
- if (condition()) {
- resolve()
- } else if (Date.now() - startTime > timeout) {
- reject(new Error(`Timeout waiting for condition`))
- } else {
- setTimeout(check, 100)
- }
- }
-
- check()
- })
-}
diff --git a/novalon-manage-web/src/__tests__/utils/errorHandler.test.ts b/novalon-manage-web/src/__tests__/utils/errorHandler.test.ts
deleted file mode 100644
index de90eb8..0000000
--- a/novalon-manage-web/src/__tests__/utils/errorHandler.test.ts
+++ /dev/null
@@ -1,233 +0,0 @@
-import { describe, it, expect, beforeEach, vi } from 'vitest'
-import { ElMessage } from 'element-plus'
-import { handleApiError, ApiErrorHandler } from '@/utils/errorHandler'
-
-vi.mock('element-plus', () => ({
- ElMessage: {
- error: vi.fn(),
- success: vi.fn(),
- },
-}))
-
-describe('errorHandler', () => {
- beforeEach(() => {
- vi.clearAllMocks()
- vi.stubGlobal('localStorage', {
- removeItem: vi.fn(),
- })
- vi.stubGlobal('window', {
- location: { href: '' },
- })
- })
-
- describe('handleApiError', () => {
- it('should call ApiErrorHandler.handle', () => {
- const mockError = { response: { status: 500, data: {} } }
- const handleSpy = vi.spyOn(ApiErrorHandler, 'handle')
-
- handleApiError(mockError)
-
- expect(handleSpy).toHaveBeenCalledWith(mockError)
- })
- })
-
- describe('ApiErrorHandler.handle', () => {
- it('should handle network error', () => {
- const mockError = new Error('Network Error')
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('网络连接失败,请检查网络设置')
- expect(consoleSpy).toHaveBeenCalledWith('Network Error:', mockError)
- })
-
- it('should handle 400 Bad Request', () => {
- const mockError = {
- response: {
- status: 400,
- data: { message: 'Invalid parameters' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('Invalid parameters')
- expect(consoleSpy).toHaveBeenCalledWith('Bad Request:', mockError.response.data)
- })
-
- it('should handle 401 Unauthorized', () => {
- const mockError = {
- response: {
- status: 401,
- data: { message: 'Unauthorized' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('登录已过期,请重新登录')
- expect(localStorage.removeItem).toHaveBeenCalledWith('token')
- expect(window.location.href).toBe('/login')
- expect(consoleSpy).toHaveBeenCalledWith('Unauthorized:', mockError.response.data)
- })
-
- it('should handle 403 Forbidden', () => {
- const mockError = {
- response: {
- status: 403,
- data: { message: 'Access denied' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('没有权限访问该资源')
- expect(consoleSpy).toHaveBeenCalledWith('Forbidden:', mockError.response.data)
- })
-
- it('should handle 404 Not Found', () => {
- const mockError = {
- response: {
- status: 404,
- data: { message: 'Resource not found' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('Resource not found')
- expect(consoleSpy).toHaveBeenCalledWith('Not Found:', mockError.response.data)
- })
-
- it('should handle 409 Conflict', () => {
- const mockError = {
- response: {
- status: 409,
- data: { message: 'Resource conflict' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('Resource conflict')
- expect(consoleSpy).toHaveBeenCalledWith('Conflict:', mockError.response.data)
- })
-
- it('should handle 422 Validation Error with details', () => {
- const mockError = {
- response: {
- status: 422,
- data: {
- message: 'Validation failed',
- details: {
- username: 'Username is required',
- password: 'Password is too short',
- },
- },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('Username is required、Password is too short')
- expect(consoleSpy).toHaveBeenCalledWith('Validation Error:', mockError.response.data)
- })
-
- it('should handle 422 Validation Error without details', () => {
- const mockError = {
- response: {
- status: 422,
- data: { message: 'Validation failed' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('Validation failed')
- expect(consoleSpy).toHaveBeenCalledWith('Validation Error:', mockError.response.data)
- })
-
- it('should handle 500 Internal Server Error', () => {
- const mockError = {
- response: {
- status: 500,
- data: { message: 'Server error' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('服务器内部错误,请稍后重试')
- expect(consoleSpy).toHaveBeenCalledWith('Internal Server Error:', mockError.response.data)
- })
-
- it('should handle 502 Service Unavailable', () => {
- const mockError = {
- response: {
- status: 502,
- data: { message: 'Service unavailable' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('服务暂时不可用,请稍后重试')
- expect(consoleSpy).toHaveBeenCalledWith('Service Unavailable:', mockError.response.data)
- })
-
- it('should handle 503 Service Unavailable', () => {
- const mockError = {
- response: {
- status: 503,
- data: { message: 'Service unavailable' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('服务暂时不可用,请稍后重试')
- expect(consoleSpy).toHaveBeenCalledWith('Service Unavailable:', mockError.response.data)
- })
-
- it('should handle 504 Gateway Timeout', () => {
- const mockError = {
- response: {
- status: 504,
- data: { message: 'Gateway timeout' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('服务暂时不可用,请稍后重试')
- expect(consoleSpy).toHaveBeenCalledWith('Service Unavailable:', mockError.response.data)
- })
-
- it('should handle unknown status code', () => {
- const mockError = {
- response: {
- status: 418,
- data: { message: 'I am a teapot' },
- },
- }
- const consoleSpy = vi.spyOn(console, 'error')
-
- ApiErrorHandler.handle(mockError)
-
- expect(ElMessage.error).toHaveBeenCalledWith('I am a teapot')
- expect(consoleSpy).toHaveBeenCalledWith('Unknown Error:', mockError.response.data)
- })
- })
-})
diff --git a/novalon-manage-web/src/api/config.ts b/novalon-manage-web/src/api/config.ts
new file mode 100644
index 0000000..d9a93de
--- /dev/null
+++ b/novalon-manage-web/src/api/config.ts
@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+import type { PageResponse } from './user.api'
+
+export interface ConfigItem {
+ id: number
+ configName: string
+ configKey: string
+ configValue: string
+ configType: string
+ remark: string
+ createdAt: string
+ updatedAt: string
+}
+
+export interface CreateConfigRequest {
+ configName: string
+ configKey: string
+ configValue: string
+ configType?: string
+ remark?: string
+}
+
+export interface UpdateConfigRequest {
+ configName?: string
+ configKey?: string
+ configValue?: string
+ configType?: string
+ remark?: string
+}
+
+export interface ConfigPageRequest {
+ page: number
+ size: number
+ configName?: string
+ configKey?: string
+ configType?: string
+}
+
+export const configApi = {
+ getAll: () =>
+ request.get('/sys/config'),
+
+ getPage: (params: ConfigPageRequest) =>
+ request.get>('/sys/config/page', { params }),
+
+ getById: (id: number) =>
+ request.get(`/sys/config/${id}`),
+
+ getByKey: (configKey: string) =>
+ request.get(`/sys/config/key/${configKey}`),
+
+ create: (data: CreateConfigRequest) =>
+ request.post('/sys/config', data),
+
+ update: (id: number, data: UpdateConfigRequest) =>
+ request.put(`/sys/config/${id}`, data),
+
+ delete: (id: number) =>
+ request.delete(`/sys/config/${id}`),
+}
diff --git a/novalon-manage-web/src/api/dict.ts b/novalon-manage-web/src/api/dict.ts
new file mode 100644
index 0000000..1cab995
--- /dev/null
+++ b/novalon-manage-web/src/api/dict.ts
@@ -0,0 +1,96 @@
+import request from '@/utils/request'
+import type { PageResponse } from './user.api'
+
+export interface DictType {
+ id: number
+ dictName: string
+ dictType: string
+ status: number
+ remark: string
+ createdAt: string
+ updatedAt: string
+}
+
+export interface DictData {
+ id: number
+ dictType: string
+ dictLabel: string
+ dictValue: string
+ sort: number
+ status: number
+ remark: string
+ createdAt: string
+ updatedAt: string
+}
+
+export interface CreateDictTypeRequest {
+ dictName: string
+ dictType: string
+ status?: number
+ remark?: string
+}
+
+export interface UpdateDictTypeRequest {
+ dictName?: string
+ dictType?: string
+ status?: number
+ remark?: string
+}
+
+export interface CreateDictDataRequest {
+ dictType: string
+ dictLabel: string
+ dictValue: string
+ sort?: number
+ status?: number
+ remark?: string
+}
+
+export interface UpdateDictDataRequest {
+ dictType?: string
+ dictLabel?: string
+ dictValue?: string
+ sort?: number
+ status?: number
+ remark?: string
+}
+
+export interface DictPageRequest {
+ page: number
+ size: number
+ dictName?: string
+ dictType?: string
+ status?: string
+}
+
+export const dictApi = {
+ getTypes: () =>
+ request.get('/dict/types'),
+
+ getTypeById: (id: number) =>
+ request.get(`/dict/types/${id}`),
+
+ createType: (data: CreateDictTypeRequest) =>
+ request.post('/dict/types', data),
+
+ updateType: (id: number, data: UpdateDictTypeRequest) =>
+ request.put(`/dict/types/${id}`, data),
+
+ deleteType: (id: number) =>
+ request.delete(`/dict/types/${id}`),
+
+ getDataByType: (dictType: string) =>
+ request.get(`/dict/data/type/${dictType}`),
+
+ getDataPage: (params: DictPageRequest & { dictType: string }) =>
+ request.get>('/dict/data/page', { params }),
+
+ createData: (data: CreateDictDataRequest) =>
+ request.post('/dict/data', data),
+
+ updateData: (id: number, data: UpdateDictDataRequest) =>
+ request.put(`/dict/data/${id}`, data),
+
+ deleteData: (id: number) =>
+ request.delete(`/dict/data/${id}`),
+}
diff --git a/novalon-manage-web/src/api/file.ts b/novalon-manage-web/src/api/file.ts
new file mode 100644
index 0000000..a5b30cc
--- /dev/null
+++ b/novalon-manage-web/src/api/file.ts
@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+import type { PageResponse } from './user.api'
+
+export interface FileInfo {
+ id: number
+ fileName: string
+ filePath: string
+ fileSize: number
+ fileType: string
+ mimeType: string
+ uploadedBy: string
+ createdAt: string
+}
+
+export interface FilePageRequest {
+ page: number
+ size: number
+ fileName?: string
+ fileType?: string
+}
+
+export const fileApi = {
+ getPage: (params: FilePageRequest) =>
+ request.get>('/files/page', { params }),
+
+ upload: (file: File) => {
+ const formData = new FormData()
+ formData.append('file', file)
+ return request.post('/files/upload', formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ })
+ },
+
+ delete: (id: number) =>
+ request.delete(`/files/${id}`),
+
+ download: (id: number) =>
+ request.get(`/files/download/${id}`, { responseType: 'blob' }),
+}
diff --git a/novalon-manage-web/src/api/loginLog.ts b/novalon-manage-web/src/api/loginLog.ts
new file mode 100644
index 0000000..9acdae8
--- /dev/null
+++ b/novalon-manage-web/src/api/loginLog.ts
@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+import type { PageResponse } from './user.api'
+import { NoticeStatus } from '@/constants/status'
+
+export interface Notice {
+ id: number
+ title: string
+ content: string
+ type: string
+ status: NoticeStatus
+ createdBy: string
+ createdAt: string
+ updatedAt: string
+}
+
+export interface CreateNoticeRequest {
+ title: string
+ content: string
+ type?: string
+ status?: NoticeStatus
+}
+
+export interface UpdateNoticeRequest {
+ title?: string
+ content?: string
+ type?: string
+ status?: NoticeStatus
+}
+
+export interface NoticePageRequest {
+ page: number
+ size: number
+ title?: string
+ type?: string
+ status?: string
+}
+
+export const noticeApi = {
+ getPage: (params: NoticePageRequest) =>
+ request.get>('/notice/page', { params }),
+
+ getById: (id: number) =>
+ request.get(`/notice/${id}`),
+
+ create: (data: CreateNoticeRequest) =>
+ request.post('/notice', data),
+
+ update: (id: number, data: UpdateNoticeRequest) =>
+ request.put(`/notice/${id}`, data),
+
+ delete: (id: number) =>
+ request.delete(`/notice/${id}`),
+}
diff --git a/novalon-manage-web/src/api/menu.ts b/novalon-manage-web/src/api/menu.ts
new file mode 100644
index 0000000..0acd6c0
--- /dev/null
+++ b/novalon-manage-web/src/api/menu.ts
@@ -0,0 +1,65 @@
+import request from '@/utils/request'
+import { MenuStatus } from '@/constants/status'
+
+export interface MenuItem {
+ id: number
+ name: string
+ path: string
+ icon: string
+ component: string
+ parentId: number
+ sort: number
+ type: 'directory' | 'menu' | 'button'
+ permission: string
+ status: MenuStatus
+ visible: boolean
+ children?: MenuItem[]
+ createdAt: string
+ updatedAt: string
+}
+
+export interface CreateMenuRequest {
+ name: string
+ path?: string
+ icon?: string
+ component?: string
+ parentId: number
+ sort: number
+ type: 'directory' | 'menu' | 'button'
+ permission?: string
+ status?: MenuStatus
+ visible?: boolean
+}
+
+export interface UpdateMenuRequest {
+ name?: string
+ path?: string
+ icon?: string
+ component?: string
+ parentId?: number
+ sort?: number
+ type?: 'directory' | 'menu' | 'button'
+ permission?: string
+ status?: MenuStatus
+ visible?: boolean
+}
+
+export const menuApi = {
+ getAll: () =>
+ request.get