diff --git a/src/components/GenerateLessonDialog.test.ts b/src/components/GenerateLessonDialog.test.ts new file mode 100644 index 0000000..56835ef --- /dev/null +++ b/src/components/GenerateLessonDialog.test.ts @@ -0,0 +1,50 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' +import GenerateLessonDialog from './GenerateLessonDialog.vue' + +describe('GenerateLessonDialog', () => { + it('disables submit until a topic is entered', async () => { + const wrapper = mount(GenerateLessonDialog, { props: { loading: false, error: null } }) + + const submit = wrapper.findAll('button')[0]! + expect(submit.attributes('disabled')).toBeDefined() + + await wrapper.get('input').setValue('CSS 弹性布局') + expect(submit.attributes('disabled')).toBeUndefined() + }) + + it('emits submit with the trimmed topic', async () => { + const wrapper = mount(GenerateLessonDialog, { props: { loading: false, error: null } }) + + await wrapper.get('input').setValue(' CSS 弹性布局 ') + await wrapper.findAll('button')[0]!.trigger('click') + + expect(wrapper.emitted('submit')).toEqual([['CSS 弹性布局']]) + }) + + it('shows a loading state and disables interaction', () => { + const wrapper = mount(GenerateLessonDialog, { props: { loading: true, error: null } }) + + expect(wrapper.get('input').attributes('disabled')).toBeDefined() + expect(wrapper.findAll('button')[0]!.text()).toContain('生成中') + expect(wrapper.findAll('button')[0]!.attributes('disabled')).toBeDefined() + }) + + it('shows an error message and allows retry without closing', async () => { + const wrapper = mount(GenerateLessonDialog, { props: { loading: false, error: 'Deepseek 请求失败。' } }) + + expect(wrapper.text()).toContain('Deepseek 请求失败。') + expect(wrapper.findAll('button')[0]!.attributes('disabled')).toBeDefined() + + await wrapper.get('input').setValue('CSS 弹性布局') + expect(wrapper.findAll('button')[0]!.attributes('disabled')).toBeUndefined() + }) + + it('emits cancel', async () => { + const wrapper = mount(GenerateLessonDialog, { props: { loading: false, error: null } }) + + await wrapper.findAll('button')[1]!.trigger('click') + + expect(wrapper.emitted('cancel')).toHaveLength(1) + }) +}) diff --git a/src/components/GenerateLessonDialog.vue b/src/components/GenerateLessonDialog.vue new file mode 100644 index 0000000..a04068b --- /dev/null +++ b/src/components/GenerateLessonDialog.vue @@ -0,0 +1,44 @@ + + + + + + 生成教案 + 输入主题,AI 将生成一份符合模板结构的教案,加入当前整本末尾。 + + {{ error }} + + + {{ loading ? '生成中…' : '生成' }} + + 取消 + + + +
输入主题,AI 将生成一份符合模板结构的教案,加入当前整本末尾。
{{ error }}