You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
5.6 KiB

import Wizard from '@/views/survey/components/SurveyCreateWizard.vue'
import SurveyList from '@/views/survey/index.vue'
import { surveyCopyDraft } from '@/utils/surveyCopy'
import { show, save } from '@/api/survey/evaluations'
import { index as questionIndex } from '@/api/survey/index'
jest.mock('@/api/survey/evaluations', () => ({ show: jest.fn(), save: jest.fn(), index: jest.fn(), destroy: jest.fn(), qrCode: jest.fn() }))
jest.mock('@/api/survey/index', () => ({ index: jest.fn(), save: jest.fn(), destroy: jest.fn(), reorder: jest.fn() }))
const source = {
id: 10, title: '培训问卷', desc: '<p>说明</p>', course_id: '11', type_id: '3', audience_course_ids: [11, 12],
start_time: '2026-12-01 00:00:00', end_time: '2026-12-31 23:59:59', status: 1,
course_content_evaluation_forms_count: 20, qrCodeData: 'source-qr'
}
const questions = [
{ id: 20, course_content_evaluation_id: 10, field: 'attend', name: '是否参加', edit_input: 'radio', sort: 1, select_item: [{ key: 'yes', value: '是' }] },
{ id: 21, field: 'group', name: '组合题', edit_input: 'multi_dimension', sort: 2, dimensions: [{ field: 'count', edit_input: 'number', config: { min: 0 } }], config: { visible_when: { field: 'attend', values: ['yes'], required_values: ['yes'] } } }
]
function wizard() {
return {
...Wizard.data(), ...Wizard.methods,
base: { requestToForm: (data, form) => Object.fromEntries(Object.keys(form).map(key => [key, data[key] === undefined ? form[key] : data[key]])) },
compareTime: () => false,
$emit: jest.fn(), $message: jest.fn(), $refs: { dialog: { reset: jest.fn() } }, $nextTick: callback => callback()
}
}
describe('Copy a questionnaire into the new-survey dialog', () => {
beforeEach(() => jest.resetAllMocks())
it('opens an independent draft with all editable basics and deep-copied question settings, without saving', async() => {
show.mockResolvedValue(source)
questionIndex.mockResolvedValue({ data: questions })
const state = wizard()
await state.openCopy(10, [{ id: 11, is_arrange: 0 }])
expect(state.isShow).toBe(true)
expect(state.type).toBe('add')
expect(state.id).toBe('')
expect(state.form.id).toBeUndefined()
expect(state.form.title).toBe('培训问卷(副本)')
expect(state.form.status).toBe(0)
expect(state.form.desc).toBe(source.desc)
expect(state.form.audience_course_ids).toEqual([11, 12])
expect(state.form.start_time).toBe(source.start_time)
expect(state.form.qrCodeData).toBeUndefined()
expect(state.form.course_content_evaluation_forms_count).toBeUndefined()
expect(state.questionList[0].id).not.toBe(20)
state.questionList[1].dimensions[0].config.min = 5
state.questionList[1].config.visible_when.values.push('no')
expect(questions[1].dimensions[0].config.min).toBe(0)
expect(questions[1].config.visible_when.values).toEqual(['yes'])
expect(save).not.toHaveBeenCalled()
Wizard.watch.isShow.call(state, false)
expect(state.copyFromId).toBe('')
expect(state.questionList).toEqual([])
})
it('creates the copy only on save, reloads new question IDs and updates that same new survey on subsequent saves', async() => {
show.mockResolvedValue(source)
questionIndex.mockResolvedValueOnce({ data: questions }).mockResolvedValueOnce({ data: questions.map(question => ({ ...question, id: question.id + 100, course_content_evaluation_id: 100 })) })
const state = wizard()
await state.openCopy(10, [{ id: 11, is_arrange: 0 }])
save.mockResolvedValue({ ...source, id: 100, status: 0 })
await state.submit()
expect(save.mock.calls[0][0].copy_from_id).toBe(10)
expect(save.mock.calls[0][0].id).toBeUndefined()
expect(state.id).toBe(100)
expect(state.questionLinkIds.course_content_evaluation_id).toBe(100)
expect(state.questionList[0].id).toBe(120)
state.form.title = '修改新问卷标题'
await state.submit()
expect(save.mock.calls[1][0].id).toBe(100)
expect(save.mock.calls[1][0].copy_from_id).toBeUndefined()
expect(questionIndex).toHaveBeenCalledTimes(2)
})
it('restores selected schedules without retaining the original questionnaire link', async() => {
show.mockResolvedValue({ ...source, course_content_id: '8,9' })
questionIndex.mockResolvedValue({ data: questions })
const state = wizard()
state.filterSurveys = async() => { state.courseContentList = [{ id: 8 }, { id: 9 }, { id: 10 }] }
await state.openCopy(10, [{ id: 11, is_arrange: 1 }])
expect(state.courseContentList.map(item => !!item.checked)).toEqual([true, true, false])
expect(state.selectedCourseSchedules.map(item => item.id)).toEqual([8, 9])
expect(state.questionLinkIds.course_content_evaluation_id).toBe('')
})
it('does not open or create a copy when loading fails and unlocks the list copy menu for retry', async() => {
const state = wizard()
show.mockRejectedValue(new Error('missing source'))
questionIndex.mockResolvedValue({ data: [] })
await expect(state.openCopy(10, [])).rejects.toThrow('missing source')
expect(state.isShow).toBe(false)
expect(save).not.toHaveBeenCalled()
const list = { copyingSurveyId: null, courseList: [], $refs: { SurveyCreateWizard: state }, $message: { error: jest.fn() } }
await SurveyList.methods.duplicateSurvey.call(list, 10)
expect(list.copyingSurveyId).toBeNull()
expect(list.$message.error).toHaveBeenCalled()
})
it('preserves selected schedule links and keeps the draft name within the existing title limit', () => {
const draft = surveyCopyDraft({ ...source, title: '题'.repeat(255), course_content_id: '8,9' }, questions)
expect(draft.form.course_content_id).toBe('8,9')
expect(draft.form.title.length).toBeLessThanOrEqual(255)
})
})