import { describe, it, expect, vi, beforeEach } from 'vitest' import { mount } from '@vue/test-utils' import { createRouter, createMemoryHistory } from 'vue-router' import App from '../App.vue' global.fetch = vi.fn() // Mock localStorage const localStorageMock = (() => { let store = {} return { getItem: vi.fn(k => store[k] || null), setItem: vi.fn((k, v) => { store[k] = v }), removeItem: vi.fn(k => { delete store[k] }), clear: vi.fn(() => { store = {} }), } })() Object.defineProperty(global, 'localStorage', { value: localStorageMock }) function createMockRouter() { return createRouter({ history: createMemoryHistory(), routes: [{ path: '/', component: { template: '
' } }], }) } describe('App.vue', () => { beforeEach(() => { vi.clearAllMocks() localStorageMock.getItem.mockImplementation(k => null) }) it('显示标题', async () => { const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) expect(wrapper.text()).toContain('iOS 自动打包服务') }) it('未登录时显示登录按钮', async () => { const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) expect(wrapper.find('.btn-login').exists()).toBe(true) }) it('已登录时显示退出按钮', async () => { localStorageMock.getItem.mockReturnValue('true') const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) expect(wrapper.find('.btn-logout').exists()).toBe(true) expect(wrapper.find('.btn-login').exists()).toBe(false) }) it('点击登录按钮弹出登录框', async () => { const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) await wrapper.find('.btn-login').trigger('click') expect(wrapper.find('.login-modal').exists()).toBe(true) }) it('登录框不展示默认管理员账号密码', async () => { const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) await wrapper.find('.btn-login').trigger('click') expect(wrapper.find('.login-modal').text()).not.toContain('默认账号') expect(wrapper.find('.login-modal').text()).not.toContain('admin123') }) it('登录成功后更新状态', async () => { fetch.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ token: 'admin-token', is_admin: true }), }) const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) await wrapper.find('.btn-login').trigger('click') await wrapper.find('.login-modal input[type="text"]').setValue('admin') await wrapper.find('.login-modal input[type="password"]').setValue('admin123') await wrapper.find('.login-btn').trigger('click') await wrapper.vm.$nextTick() expect(wrapper.find('.btn-logout').exists()).toBe(true) }) it('登录失败显示错误', async () => { fetch.mockResolvedValueOnce({ ok: false }) window.alert = vi.fn() const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) await wrapper.find('.btn-login').trigger('click') await wrapper.find('.login-modal input[type="text"]').setValue('admin') await wrapper.find('.login-modal input[type="password"]').setValue('wrong') await wrapper.find('.login-btn').trigger('click') await wrapper.vm.$nextTick() expect(window.alert).toHaveBeenCalledWith('用户名或密码错误') }) it('点击退出清除登录状态', async () => { localStorageMock.getItem.mockReturnValue('true') const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) await wrapper.find('.btn-logout').trigger('click') expect(wrapper.find('.btn-login').exists()).toBe(true) expect(localStorageMock.removeItem).toHaveBeenCalledWith('isAdmin') }) it('已登录时显示管理导航', async () => { localStorageMock.getItem.mockReturnValue('true') const router = createMockRouter() const wrapper = mount(App, { global: { plugins: [router] }, }) const links = wrapper.findAll('.nav a') const texts = links.map(l => l.text()) expect(texts).toContain('管理') }) })