feat: integrate learning module
This commit is contained in:
137
tests/unit/learning-page.test.tsx
Normal file
137
tests/unit/learning-page.test.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { Learning } from '@/pages/Learning';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
const fetchLearningCoursesMock = vi.hoisted(() => vi.fn());
|
||||
const fetchMyLearningCoursesMock = vi.hoisted(() => vi.fn());
|
||||
const listInstalledLearningCoursesMock = vi.hoisted(() => vi.fn());
|
||||
const downloadLearningCourseMock = vi.hoisted(() => vi.fn());
|
||||
const startLearningGenerationMock = vi.hoisted(() => vi.fn());
|
||||
const fetchLearningGenerationMock = vi.hoisted(() => vi.fn());
|
||||
const finalizeLearningGenerationMock = vi.hoisted(() => vi.fn());
|
||||
const cancelLearningGenerationMock = vi.hoisted(() => vi.fn());
|
||||
const resumeLearningGenerationMock = vi.hoisted(() => vi.fn());
|
||||
const transcribeLearningSpeechMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('@/lib/learning', () => ({
|
||||
fetchLearningCourses: fetchLearningCoursesMock,
|
||||
fetchMyLearningCourses: fetchMyLearningCoursesMock,
|
||||
listInstalledLearningCourses: listInstalledLearningCoursesMock,
|
||||
downloadLearningCourse: downloadLearningCourseMock,
|
||||
startLearningGeneration: startLearningGenerationMock,
|
||||
fetchLearningGeneration: fetchLearningGenerationMock,
|
||||
finalizeLearningGeneration: finalizeLearningGenerationMock,
|
||||
cancelLearningGeneration: cancelLearningGenerationMock,
|
||||
resumeLearningGeneration: resumeLearningGenerationMock,
|
||||
transcribeLearningSpeech: transcribeLearningSpeechMock,
|
||||
}));
|
||||
|
||||
describe('Learning page', () => {
|
||||
beforeEach(() => {
|
||||
fetchLearningCoursesMock.mockReset();
|
||||
fetchMyLearningCoursesMock.mockReset();
|
||||
listInstalledLearningCoursesMock.mockReset();
|
||||
downloadLearningCourseMock.mockReset();
|
||||
startLearningGenerationMock.mockReset();
|
||||
fetchLearningGenerationMock.mockReset();
|
||||
finalizeLearningGenerationMock.mockReset();
|
||||
cancelLearningGenerationMock.mockReset();
|
||||
resumeLearningGenerationMock.mockReset();
|
||||
transcribeLearningSpeechMock.mockReset();
|
||||
window.localStorage.clear();
|
||||
listInstalledLearningCoursesMock.mockResolvedValue([]);
|
||||
fetchMyLearningCoursesMock.mockResolvedValue([]);
|
||||
});
|
||||
|
||||
it('renders published courses returned by Works Square', async () => {
|
||||
fetchLearningCoursesMock.mockResolvedValue([{
|
||||
id: 'course-1',
|
||||
origin: 'ops_large',
|
||||
status: 'published',
|
||||
title: 'Python 基础入门',
|
||||
summary: '通过互动讲解掌握 Python。',
|
||||
language: 'zh-CN',
|
||||
contentHash: 'a'.repeat(64),
|
||||
archiveSha256: 'b'.repeat(64),
|
||||
archiveBytes: 1024,
|
||||
formatVersion: 1,
|
||||
minPlayerVersion: '1.0.0',
|
||||
sceneCount: 10,
|
||||
capabilities: { sceneKinds: ['slide', 'interactive', 'quiz'], hasAudio: false, hasWhiteboard: true, hasAgent: true },
|
||||
createdAt: '2026-08-16T00:00:00Z',
|
||||
publishedAt: '2026-08-16T00:00:00Z',
|
||||
}]);
|
||||
|
||||
render(<MemoryRouter><Learning /></MemoryRouter>);
|
||||
|
||||
expect(screen.getByRole('status')).toHaveTextContent('正在读取课程');
|
||||
expect(await screen.findByTestId('learning-course-course-1')).toHaveTextContent('Python 基础入门');
|
||||
expect(screen.getByText('10 个学习场景')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows a retryable error state', async () => {
|
||||
fetchLearningCoursesMock.mockRejectedValue(new Error('连接失败'));
|
||||
|
||||
render(<MemoryRouter><Learning /></MemoryRouter>);
|
||||
|
||||
await waitFor(() => expect(screen.getByRole('alert')).toHaveTextContent('连接失败'));
|
||||
expect(screen.getByRole('button', { name: '重试' })).toBeEnabled();
|
||||
});
|
||||
|
||||
it('opens the full single-course workbench only from the generate route', async () => {
|
||||
fetchLearningCoursesMock.mockResolvedValue([]);
|
||||
render(<MemoryRouter initialEntries={['/learning?generate=1']}><Learning /></MemoryRouter>);
|
||||
await screen.findByText('暂时还没有已发布课程');
|
||||
|
||||
expect(screen.getByRole('dialog')).toHaveTextContent('今天想学会什么?');
|
||||
expect(screen.getByLabelText('课程需求')).toBeEnabled();
|
||||
expect(screen.getByRole('button', { name: '后台生成' })).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: '联网搜索' })).toHaveAttribute('aria-pressed', 'false');
|
||||
expect(screen.getByRole('button', { name: '语音讲解' })).toHaveAttribute('aria-pressed', 'true');
|
||||
expect(screen.getByRole('button', { name: '互动模式' })).toHaveAttribute('aria-pressed', 'true');
|
||||
expect(screen.getByRole('button', { name: '职教任务' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '语音输入' })).toBeInTheDocument();
|
||||
expect(screen.queryByText(/供应商选择/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('submits every canonical option explicitly to the background job', async () => {
|
||||
fetchLearningCoursesMock.mockResolvedValue([]);
|
||||
startLearningGenerationMock.mockResolvedValue({
|
||||
contractVersion: 2,
|
||||
jobId: 'job-1',
|
||||
status: 'queued',
|
||||
mode: 'single',
|
||||
done: false,
|
||||
});
|
||||
fetchLearningGenerationMock.mockImplementation(() => new Promise(() => undefined));
|
||||
render(<MemoryRouter initialEntries={['/learning?generate=1']}><Learning /></MemoryRouter>);
|
||||
|
||||
fireEvent.change(await screen.findByLabelText('课程需求'), { target: { value: '用互动练习教我二次函数' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '联网搜索' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '生成图片' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '职教任务' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '后台生成' }));
|
||||
|
||||
await waitFor(() => expect(startLearningGenerationMock).toHaveBeenCalledWith({
|
||||
requirement: '用互动练习教我二次函数',
|
||||
enableWebSearch: true,
|
||||
enableImageGeneration: true,
|
||||
enableVideoGeneration: false,
|
||||
enableTTS: true,
|
||||
interactiveMode: true,
|
||||
taskEngineMode: true,
|
||||
}, []));
|
||||
});
|
||||
|
||||
it('renders the course plaza without adding another sidebar', async () => {
|
||||
fetchLearningCoursesMock.mockResolvedValue([]);
|
||||
|
||||
render(<MemoryRouter><Learning /></MemoryRouter>);
|
||||
|
||||
await screen.findByText('暂时还没有已发布课程');
|
||||
expect(screen.getByRole('region', { name: '课程广场' })).toBeInTheDocument();
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('complementary')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user