From 74687c1de19ee8eba26aaf6ee404d9e7751ca10f Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 15 Jun 2026 20:14:07 -0600 Subject: [PATCH] feat: add generate lesson dialog --- src/components/GenerateLessonDialog.test.ts | 50 +++++++++++++++++++++ src/components/GenerateLessonDialog.vue | 44 ++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/components/GenerateLessonDialog.test.ts create mode 100644 src/components/GenerateLessonDialog.vue 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 @@ + + +