import { describe, it, expect, beforeEach, vi } from 'vitest' import { mount, flushPromises } from '@vue/test-utils' import { createRouter, createMemoryHistory } from 'vue-router' import ElementPlus from 'element-plus' import UsersView from '../src/views/UsersView.vue' import LoginView from '../src/views/LoginView.vue' import WelcomeView from '../src/views/WelcomeView.vue' import axios from 'axios' vi.mock('axios', () => ({ default: { get: vi.fn(), post: vi.fn(), put: vi.fn(), }, })) const mockedAxios = axios const makeRouter = () => createRouter({ history: createMemoryHistory(), routes: [ { path: '/', name: 'login', component: LoginView }, { path: '/welcome', name: 'welcome', component: WelcomeView }, { path: '/users', name: 'users', component: UsersView }, ], }) describe('UsersView', () => { beforeEach(() => { localStorage.clear() vi.clearAllMocks() }) it('redirects to login when token is missing', async () => { const router = makeRouter() router.push('/users') await router.isReady() mount(UsersView, { global: { plugins: [router, ElementPlus], }, }) await flushPromises() expect(router.currentRoute.value.name).toBe('login') }) it('renders user rows when token exists and API succeeds', async () => { localStorage.setItem('ars-token', 'jwt-token') mockedAxios.get.mockResolvedValue({ data: { data: [ { id: 1, name: 'Alice', email: 'alice@example.com', is_active: true }, { id: 2, name: 'Bob', email: 'bob@example.com', is_active: false }, ], current_page: 1, per_page: 10, total: 2, }, }) const router = makeRouter() router.push('/users') await router.isReady() const wrapper = mount(UsersView, { global: { plugins: [router, ElementPlus], }, }) await flushPromises() const rows = wrapper.findAll('[data-testid="user-row"]') expect(rows).toHaveLength(2) expect(wrapper.text()).toContain('Alice') expect(wrapper.text()).toContain('bob@example.com') }) it('deactivates a user via API and updates UI', async () => { localStorage.setItem('ars-token', 'jwt-token') mockedAxios.get.mockResolvedValue({ data: { data: [{ id: 1, name: 'Alice', email: 'alice@example.com', is_active: true }], current_page: 1, per_page: 10, total: 1, }, }) mockedAxios.post.mockResolvedValue({ data: { id: 1, is_active: false } }) const router = makeRouter() router.push('/users') await router.isReady() const wrapper = mount(UsersView, { global: { plugins: [router, ElementPlus], }, }) await flushPromises() await wrapper.find('[data-testid="toggle-btn-1"]').trigger('click') await flushPromises() expect(mockedAxios.post).toHaveBeenCalledWith( 'http://localhost:8000/api/users/1/deactivate', {}, expect.objectContaining({ headers: expect.objectContaining({ Authorization: 'Bearer jwt-token' }), }) ) expect(wrapper.text()).toContain('停用') }) it('edits a user and sends PUT payload', async () => { localStorage.setItem('ars-token', 'jwt-token') mockedAxios.get.mockResolvedValue({ data: { data: [{ id: 1, name: 'Alice', email: 'alice@example.com', is_active: true }], current_page: 1, per_page: 10, total: 1, }, }) mockedAxios.put.mockResolvedValue({ data: { id: 1, name: 'Alice Updated', email: 'alice@new.com', is_active: true }, }) const router = makeRouter() router.push('/users') await router.isReady() const wrapper = mount(UsersView, { global: { plugins: [router, ElementPlus], }, }) await flushPromises() await wrapper.find('[data-testid="edit-btn-1"]').trigger('click') await wrapper.find('[data-testid="edit-name"]').setValue('Alice Updated') await wrapper.find('[data-testid="edit-email"]').setValue('alice@new.com') await wrapper.find('[data-testid="save-user"]').trigger('click') await flushPromises() expect(mockedAxios.put).toHaveBeenCalledWith( 'http://localhost:8000/api/users/1', expect.objectContaining({ name: 'Alice Updated', email: 'alice@new.com', }), expect.objectContaining({ headers: expect.objectContaining({ Authorization: 'Bearer jwt-token' }), }) ) expect(wrapper.text()).toContain('Alice Updated') expect(wrapper.text()).toContain('alice@new.com') }) })