import { shallowMount, createLocalVue } from '@vue/test-utils' import ElementUI from 'element-ui' import Rules from '@/views/course/components/CourseFieldRules.vue' import Answer from '@/views/course/components/CourseAnswerField.vue' import SurveySettings from '@/views/survey/components/SurveyFieldSettings.vue' import CourseEditor from '@/views/course/components/editPane.vue' import { save as saveCourseField } from '@/api/course/form' import { prepareCourseFields, visibleCourseFields, courseFormError, courseSubmission } from '@/utils/courseForm' jest.mock('@/api/course/form', () => ({ save: jest.fn(() => Promise.resolve({ id: 7 })), destroy: jest.fn() })) const localVue = createLocalVue() localVue.use(ElementUI) function mountForm(component, props, mocks = {}) { const state = new localVue({ data() { return { fields: props } } }) return shallowMount(component, { localVue, propsData: state.fields, mocks }) } const choices = [{ key: 'tax', value: '财税' }, { key: 'legal', value: '法律' }, { key: 'ip', value: '知识产权' }] describe('Course and survey form compatibility', () => { it('allows new option keys and ordering after reopening a saved field, while locking only original keys', async () => { saveCourseField.mockClear() const field = { id: 7, field: 'venues', name: '会场', edit_input: 'checkbox', select_item: choices.slice(0, 2).map(option => ({ ...option })), config: { option_notes: { tax: { placeholder: '咨询内容', required: false } } } } const wrapper = shallowMount(CourseEditor, { localVue, propsData: { rules: {}, types: [] }, methods: { getAllPara() {}, getCompanyConfig() {} }, mocks: { base: { isNull: value => value == null || String(value).trim() === '' }, $Message: { warning: jest.fn(), success: jest.fn() } } }) await wrapper.setData({ showForm: true, selectedForm: field }) const original = wrapper.vm.selectItem[0] expect(wrapper.vm.isSavedOption(original)).toBe(true) expect(wrapper.findAll('.select-item__body').at(0).find('el-input-stub').attributes('disabled')).toBeDefined() wrapper.find('el-button-stub[icon="el-icon-plus"]').vm.$emit('click') await wrapper.vm.$nextTick() const added = wrapper.vm.selectItem[2] expect(wrapper.vm.isSavedOption(added)).toBe(false) expect(wrapper.findAll('.select-item__body').at(2).find('el-input-stub').attributes('disabled')).toBeUndefined() added.key = 'new'; added.value = '其他' wrapper.vm.moveOption(2, -1) wrapper.vm.moveOption(0, 1) expect(wrapper.vm.selectItem.map(option => option.key)).toEqual(['new', 'tax', 'legal']) expect(wrapper.vm.isSavedOption(added)).toBe(false) expect(wrapper.vm.isSavedOption(original)).toBe(true) wrapper.vm.saveField() await new Promise(resolve => setTimeout(resolve, 0)) const saved = saveCourseField.mock.calls[0][0] expect(saved.select_item.map(option => option.key)).toEqual(['new', 'tax', 'legal']) expect(saved.config.option_notes.tax.placeholder).toBe('咨询内容') expect(wrapper.emitted('refreshFormItem')).toBeTruthy() await wrapper.setData({ showForm: true, selectedForm: { ...saved, id: 7 } }) expect(wrapper.vm.selectItem.every(option => wrapper.vm.isSavedOption(option))).toBe(true) wrapper.destroy() }) it('sets default minima, keeps unlimited maxima, and rejects a maximum below the minimum', () => { for (const component of [Rules, SurveySettings]) { const field = { id: 1, field: 'venues', name: '会场', edit_input: 'checkbox', config: {}, select_item: [...choices] } const warning = jest.fn() const wrapper = mountForm(component, { field }, { $message: { warning } }) expect(wrapper.findAll('el-input-number-stub').at(0).props('min')).toBe(1) if (component === Rules) { wrapper.vm.minimum(2); wrapper.vm.maximum(1); wrapper.vm.maximum(0) } else { wrapper.vm.changeMinimum(2); wrapper.vm.changeMaximum(1); wrapper.vm.changeMaximum(0) } expect(field.config.min_choices).toBe(2) expect(field.config.max_choices).toBe(0) expect(warning).toHaveBeenCalledWith('最多选择项数不能小于最少选择项数') wrapper.destroy() } }) it('configures multiple triggers and required overrides without changing existing field identifiers', () => { const parent = { id: 1, field: 'finance', sort: 1, name: '融资', edit_input: 'radio', select_item: choices } const field = { id: 2, field: 'legacy_field', sort: 2, name: '材料', edit_input: 'text', config: {} } const wrapper = mountForm(Rules, { field, fields: [parent, field] }) wrapper.vm.toggleCondition(true) wrapper.vm.changeParent('finance') field.config.visible_when.values = ['tax', 'legal'] wrapper.vm.toggleRequired(true) field.config.visible_when.required_values = ['tax'] expect(wrapper.vm.parents).toEqual([parent]) expect(field.field).toBe('legacy_field') expect(field.config.visible_when.required_values).toEqual(['tax']) wrapper.vm.changeParent('finance') expect(field.config.visible_when.values).toEqual([]) expect(field.config.visible_when.required_values).toEqual([]) wrapper.destroy() }) it('restores comma answers, shows selected supplements, enforces limits, and clears deselected notes', async () => { const fields = prepareCourseFields([{ id: 1, field: 'venues', name: '会场', edit_input: 'checkbox', value: '财税,法律', select_item: choices, config: { min_choices: 2, max_choices: 2, option_notes: { tax: { placeholder: '咨询内容', required: true } } } }]) const wrapper = mountForm(Answer, { field: fields[0] }) expect(wrapper.vm.selection).toEqual(['tax', 'legal']) expect(wrapper.find('el-input-stub').attributes('placeholder')).toBe('咨询内容') expect(courseFormError(fields)).toContain('请填写补充说明') fields[0].option_texts.tax = '问题' expect(courseFormError(fields)).toBe('') wrapper.vm.choose(['legal']) await wrapper.vm.$nextTick() expect(wrapper.find('el-input-stub').exists()).toBe(false) expect(fields[0].option_texts).toEqual({}) expect(courseFormError(fields)).toContain('至少选择2项') wrapper.destroy() }) it('keeps saved identity values and zero; hidden answers never enter profile/update payloads', () => { const fields = prepareCourseFields([ { field: 'username', name: '姓名', edit_input: 'text', belong_user: true, value: '原报名姓名' }, { field: 'parent', name: '前置', edit_input: 'radio', value: '否', select_item: [{ key: 'no', value: '否' }] }, { field: 'hidden', name: '条件题', edit_input: 'checkbox', value: '财税', config: { visible_when: { field: 'parent', values: ['yes'] } }, select_item: choices, option_texts: { tax: '旧说明' } }, { field: 'zero', name: '数量', edit_input: 'number', value: 0 } ], { username: '最新资料姓名' }) expect(fields[0].value).toBe('原报名姓名') expect(fields[3].value).toBe(0) expect(visibleCourseFields(fields).map(field => field.field)).toEqual(['username', 'parent', 'zero']) expect(courseSubmission(fields)[2]).toMatchObject({ hidden: true, value: null, option_texts: {}, selected_keys: [] }) }) })