import AccountForm from '@/views/account/accountList/components/addOrExit.vue' import RoleForm from '@/views/account/roleList/components/addOrExit.vue' import AccountList from '@/views/account/accountList/index.vue' import RoleList from '@/views/account/roleList/index.vue' import Login from '@/views/login/index.vue' import UploadImgPaste from '@/components/UploadImgPaste/index.vue' import UploadImages from '@/components/UploadComicsImgArrByOrder/index.vue' import { accAdd, accUp, accSavestatus, accList } from '@/api/account/accountList' import { roleAdd, roleUp, getRoleList } from '@/api/account/roleList' import { login, validateQR } from '@/api/login' import { mediaUploadImg } from '@/api/components/UploadImgPaste' import { upLoadFile } from '@/api/common' jest.mock('@/api/account/accountList', () => ({ accAdd: jest.fn(), accUp: jest.fn(), accSavestatus: jest.fn(), accList: jest.fn() })) jest.mock('@/api/account/roleList', () => ({ roleAdd: jest.fn(), roleUp: jest.fn(), getRoleList: jest.fn() })) jest.mock('@/api/login', () => ({ login: jest.fn(), validateQR: jest.fn() })) jest.mock('@/api/system/systemConfig', () => ({ getSyscfg: jest.fn() })) jest.mock('@/api/components/UploadImgPaste', () => ({ mediaUploadImg: jest.fn() })) jest.mock('@/api/common', () => ({ upLoadFile: jest.fn() })) function context(component, state = {}, valid = true) { return Object.assign({ $refs: { ruleForm: { validate: callback => callback(valid) }, loginForm: { validate: callback => callback(valid) } }, $message: Object.assign(jest.fn(), { success: jest.fn(), error: jest.fn() }) }, component.methods, state) } beforeEach(() => jest.resetAllMocks()) const forms = [ ['新增账号', AccountForm, accAdd, 'submitForm', 'btnLoading', 'dialogFormVisible'], ['编辑账号', AccountForm, accUp, 'submitForm', 'btnLoading', 'dialogFormVisible'], ['账号状态', AccountForm, accSavestatus, 'submitForm', 'btnLoading', 'dialogFormVisible'], ['新增角色', RoleForm, roleAdd, 'onSubmitRole', 'disabled', 'dialogVisible'], ['编辑角色', RoleForm, roleUp, 'onSubmitRole', 'disabled', 'dialogVisible'] ] forms.forEach(([title, component, request, method, flag, visible]) => { describe(title, () => { const makeForm = (valid = true) => context(component, { title, rowData: { acc: 'tester', userName: '测试账号' }, params: {}, [flag]: false, [visible]: true }, valid) const failures = [ ['业务失败', () => Promise.resolve({ code: 500 })], ['网络失败', () => Promise.reject(new Error('network error'))], ['请求同步异常', () => { throw new Error('request error') }] ] failures.forEach(([name, fail]) => { it(`${name}后结束加载、保留表单并支持重试`, async() => { const vm = makeForm() request.mockImplementationOnce(fail).mockResolvedValueOnce({ code: 200 }) await vm[method]('ruleForm') expect(vm[flag]).toBe(false) expect(vm[visible]).toBe(true) expect(vm.$message.success).not.toHaveBeenCalled() await vm[method]('ruleForm') expect(request).toHaveBeenCalledTimes(2) expect(vm[flag]).toBe(false) expect(vm[visible]).toBe(false) expect(vm.$message.success).toHaveBeenCalledTimes(1) }) }) it('请求未结束时保持加载并阻止重复提交', async() => { const vm = makeForm() let finish request.mockImplementationOnce(() => new Promise(resolve => { finish = resolve })) const pending = vm[method]('ruleForm') expect(vm[flag]).toBe(true) vm[method]('ruleForm') expect(request).toHaveBeenCalledTimes(1) finish({ code: 200 }) await pending expect(vm[flag]).toBe(false) }) it('校验失败时不请求也不进入加载', async() => { const vm = makeForm(false) await vm[method]('ruleForm') expect(request).not.toHaveBeenCalled() expect(vm[flag]).toBe(false) }) }) }) describe('账号和角色列表', () => { [ [AccountList, accList, 'getAccountData', { code: 200, data: { accRec: [], totalCount: 0 } }, 'accountData', 'accRec'], [RoleList, getRoleList, 'getRoleData', { code: 200, data: { roleRecs: [], totalCount: 0 } }, 'list', 'roleRecs'] ].forEach(([component, request, method, success, listKey, responseKey]) => { it(`${method}接口失败后可再次加载`, async() => { const vm = context(component, { loading: false, searchData: {}, param: {} }) let fail request.mockImplementationOnce(() => new Promise((resolve, reject) => { fail = reject })) .mockResolvedValueOnce(success) const pending = vm[method]() expect(vm.loading).toBe(true) fail(new Error('network error')) await pending expect(vm.loading).toBe(false) await vm[method]() expect(request).toHaveBeenCalledTimes(2) expect(vm.loading).toBe(false) }) it(`${method}关闭弹框后静默更新数据并保留当前分页`, async() => { const oldRows = [{ id: 1 }] const newRows = [{ id: 2 }] const vm = context(component, { loading: false, [listKey]: oldRows, dialogVisible: true, dialogFormVisible: true, searchData: { pageNum: 3 }, param: { pageNum: 3 } }) let finish request.mockImplementationOnce(() => new Promise(resolve => { finish = resolve })) const pending = vm.close() expect(vm.loading).toBe(false) expect(vm[listKey]).toBe(oldRows) finish({ ...success, data: { ...success.data, [responseKey]: newRows } }) await pending expect(vm.loading).toBe(false) expect(vm[listKey]).toBe(newRows) expect(vm.searchData.pageNum).toBe(3) expect(vm.param.pageNum).toBe(3) }) it(`${method}静默刷新失败时保留列表且不显示遮罩`, async() => { const oldRows = [{ id: 1 }] const vm = context(component, { loading: false, [listKey]: oldRows, searchData: {}, param: {} }) request.mockRejectedValueOnce(new Error('network error')) const pending = vm[method]({ silent: true }) expect(vm.loading).toBe(false) await pending expect(vm.loading).toBe(false) expect(vm[listKey]).toBe(oldRows) }) const outcomes = ['成功', '失败'] outcomes.forEach(outcome => { it(`${method}静默刷新${outcome}不会提前关闭普通请求遮罩`, async() => { const vm = context(component, { loading: false, searchData: {}, param: {} }) let finishNormal let finishSilent request.mockImplementationOnce(() => new Promise(resolve => { finishNormal = resolve })) .mockImplementationOnce(() => new Promise((resolve, reject) => { finishSilent = outcome === '成功' ? resolve : reject })) const normal = vm[method]() const silent = vm[method]({ silent: true }) expect(vm.loading).toBe(true) finishSilent(outcome === '成功' ? success : new Error('network error')) await silent expect(vm.loading).toBe(true) finishNormal(success) await normal expect(vm.loading).toBe(false) }) }) }) }) it('登录接口失败后复位,业务失败后同样复位', async() => { const vm = context(Login, { loading: false, loginForm: { username: 'tester', password: 'password' } }) login.mockRejectedValueOnce(new Error('network error')) .mockResolvedValueOnce({ code: 4016 }) await vm.handleLogin() expect(vm.loading).toBe(false) await vm.handleLogin() expect(vm.loading).toBe(false) expect(login).toHaveBeenCalledTimes(2) expect(vm.$message.error).toHaveBeenCalledWith('密码错误,请重新输入') }) it('二维码验证请求失败后复位', async() => { const vm = context(Login, { loading: false, loginForm: { username: 'tester' } }) validateQR.mockRejectedValueOnce(new Error('network error')) await vm.handleQrValidate() expect(vm.loading).toBe(false) expect(login).not.toHaveBeenCalled() }) it('二维码验证期间修改输入导致二次校验失败时仍释放加载状态', async() => { const vm = context(Login, { loading: false, loginForm: { username: 'tester' } }) vm.$refs.loginForm.validate = jest.fn() .mockImplementationOnce(callback => callback(true)) .mockImplementationOnce(callback => callback(false)) let finish validateQR.mockImplementationOnce(() => new Promise(resolve => { finish = resolve })) const pending = vm.handleQrValidate() expect(vm.loading).toBe(true) finish({ code: 200 }) await pending expect(vm.loading).toBe(false) expect(login).not.toHaveBeenCalled() }) it('二维码验证后由登录请求保持加载并在登录失败时释放', async() => { const vm = context(Login, { loading: false, loginForm: { username: 'tester', password: 'password' } }) validateQR.mockResolvedValueOnce({ code: 200 }) let fail login.mockImplementationOnce(() => new Promise((resolve, reject) => { fail = reject })) await vm.handleQrValidate() expect(vm.loading).toBe(true) fail(new Error('network error')) await Promise.resolve() expect(vm.loading).toBe(false) }) it('粘贴图片上传失败后复位', async() => { const vm = context(UploadImgPaste, { loading: false, file: new Blob(['image']) }) mediaUploadImg.mockRejectedValueOnce(new Error('network error')) const pending = vm.uploadPlans() expect(vm.loading).toBe(true) await pending expect(vm.loading).toBe(false) }) it('批量图片上传失败后释放本地和父组件上传状态', async() => { const vm = context(UploadImages, { loading: false, fileList: [{ name: 'image.png', raw: new Blob(['image']) }], locaImg: [], source: { cancel: jest.fn() }, $notify: { error: jest.fn() }, $emit: jest.fn() }) upLoadFile.mockRejectedValueOnce(new Error('network error')) const pending = vm.upLoad() expect(vm.loading).toBe(true) expect(vm.$emit).toHaveBeenCalledWith('setIsUploadIng', true) await pending expect(vm.loading).toBe(false) expect(vm.$emit).toHaveBeenCalledWith('setIsUploadIng', false) expect(vm.$notify.error).toHaveBeenCalledTimes(1) })