/**
* Course List Component Tests (Mobile)
* Based on pallet-perwerde tests
*
* Tests cover:
* - create_course_works
* - enroll_works
* - enroll_fails_for_archived_course
* - enroll_fails_if_already_enrolled
* - complete_course_works
* - multiple_students_can_enroll_same_course
*/
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import {
generateMockCourse,
generateMockCourseList,
generateMockEnrollment,
} from '../../../utils/mockDataGenerators';
import { buildPolkadotContextState } from '../../../utils/testHelpers';
// Mock the Course List component (adjust path as needed)
// import { CourseList } from '@/src/components/perwerde/CourseList';
describe('Course List Component (Mobile)', () => {
let mockApi: any;
beforeEach(() => {
mockApi = {
query: {
perwerde: {
courses: jest.fn(),
enrollments: jest.fn(),
courseCount: jest.fn(() => ({
toNumber: () => 5,
})),
},
},
tx: {
perwerde: {
enrollInCourse: jest.fn(() => ({
signAndSend: jest.fn((account, callback) => {
callback({ status: { isInBlock: true } });
return Promise.resolve('0x123');
}),
})),
completeCourse: jest.fn(() => ({
signAndSend: jest.fn((account, callback) => {
callback({ status: { isInBlock: true } });
return Promise.resolve('0x123');
}),
})),
},
},
};
});
afterEach(() => {
jest.clearAllMocks();
});
describe('Course Display', () => {
test('should render list of active courses', () => {
const courses = generateMockCourseList(5, 3); // 5 total, 3 active
// Mock component rendering
// const { getAllByTestId } = render();
// const courseCards = getAllByTestId('course-card');
// expect(courseCards).toHaveLength(5);
const activeCourses = courses.filter(c => c.status === 'Active');
expect(activeCourses).toHaveLength(3);
});
test('should display course status badges', () => {
const activeCourse = generateMockCourse('Active');
const archivedCourse = generateMockCourse('Archived');
// Component should show:
// - Active course: Green "Active" badge
// - Archived course: Gray "Archived" badge
});
test('should show course details (title, description, content URL)', () => {
const course = generateMockCourse('Active', 0);
expect(course).toHaveProperty('title');
expect(course).toHaveProperty('description');
expect(course).toHaveProperty('contentUrl');
// Component should display all these fields
});
test('should filter courses by status', () => {
const courses = generateMockCourseList(10, 6);
const activeCourses = courses.filter(c => c.status === 'Active');
const archivedCourses = courses.filter(c => c.status === 'Archived');
expect(activeCourses).toHaveLength(6);
expect(archivedCourses).toHaveLength(4);
// Component should have filter toggle:
// [All] [Active] [Archived]
});
});
describe('Course Enrollment', () => {
test('should show enroll button for active courses', () => {
const activeCourse = generateMockCourse('Active');
// Component should show "Enroll" button
// Button should be enabled
});
test('should disable enroll button for archived courses', () => {
// Test: enroll_fails_for_archived_course
const archivedCourse = generateMockCourse('Archived');
// Component should show "Archived" or disabled "Enroll" button
});
test('should show "Already Enrolled" state', () => {
// Test: enroll_fails_if_already_enrolled
const course = generateMockCourse('Active', 0);
const enrollment = generateMockEnrollment(0, false);
mockApi.query.perwerde.enrollments.mockResolvedValue({
unwrap: () => enrollment,
});
// Component should show "Enrolled" badge or "Continue Learning" button
// instead of "Enroll" button
});
test('should successfully enroll in course', async () => {
// Test: enroll_works
const course = generateMockCourse('Active', 0);
mockApi.query.perwerde.enrollments.mockResolvedValue({
unwrap: () => null, // Not enrolled yet
});
const tx = mockApi.tx.perwerde.enrollInCourse(course.courseId);
await expect(tx.signAndSend('address', jest.fn())).resolves.toBe('0x123');
expect(mockApi.tx.perwerde.enrollInCourse).toHaveBeenCalledWith(course.courseId);
});
test('should support multiple students enrolling in same course', () => {
// Test: multiple_students_can_enroll_same_course
const course = generateMockCourse('Active', 0);
const student1 = '5Student1...';
const student2 = '5Student2...';
const enrollment1 = generateMockEnrollment(0);
const enrollment2 = generateMockEnrollment(0);
expect(enrollment1.student).not.toBe(enrollment2.student);
// Both can enroll independently
});
test('should show enrolled courses count (max 100)', () => {
// Test: enroll_fails_when_too_many_courses
const maxCourses = 100;
const currentEnrollments = 98;
// Component should show: "Enrolled: 98/100 courses"
// Warning when approaching limit: "You can enroll in 2 more courses"
expect(currentEnrollments).toBeLessThan(maxCourses);
});
});
describe('Course Completion', () => {
test('should show completion button for enrolled students', () => {
const enrollment = generateMockEnrollment(0, false);
// Component should show:
// - "Complete Course" button
// - Progress indicator
});
test('should successfully complete course with points', async () => {
// Test: complete_course_works
const course = generateMockCourse('Active', 0);
const pointsEarned = 85;
const tx = mockApi.tx.perwerde.completeCourse(course.courseId, pointsEarned);
await expect(tx.signAndSend('address', jest.fn())).resolves.toBe('0x123');
expect(mockApi.tx.perwerde.completeCourse).toHaveBeenCalledWith(
course.courseId,
pointsEarned
);
});
test('should show completed course with certificate', () => {
const completedEnrollment = generateMockEnrollment(0, true);
// Component should display:
// - "Completed" badge (green)
// - Points earned: "85 points"
// - "View Certificate" button
// - Completion date
});
test('should prevent completing course twice', () => {
// Test: complete_course_fails_if_already_completed
const completedEnrollment = generateMockEnrollment(0, true);
mockApi.query.perwerde.enrollments.mockResolvedValue({
unwrap: () => completedEnrollment,
});
// "Complete Course" button should be hidden or disabled
});
test('should prevent completing without enrollment', () => {
// Test: complete_course_fails_without_enrollment
mockApi.query.perwerde.enrollments.mockResolvedValue({
unwrap: () => null,
});
// "Complete Course" button should not appear
// Only "Enroll" button should be visible
});
});
describe('Course Categories', () => {
test('should categorize courses (Blockchain, Programming, Kurdistan Culture)', () => {
// Courses can have categories
const categories = ['Blockchain', 'Programming', 'Kurdistan Culture', 'History'];
// Component should show category filter pills
});
test('should filter courses by category', () => {
const courses = generateMockCourseList(10, 8);
// Mock categories
courses.forEach((course, index) => {
(course as any).category = ['Blockchain', 'Programming', 'Culture'][index % 3];
});
const blockchainCourses = courses.filter((c: any) => c.category === 'Blockchain');
// Component should filter when category pill is tapped
});
});
describe('Course Progress', () => {
test('should show enrollment progress (enrolled but not completed)', () => {
const enrollment = generateMockEnrollment(0, false);
// Component should show:
// - "In Progress" badge
// - Start date
// - "Continue Learning" button
});
test('should track completion percentage if available', () => {
// Future feature: track lesson completion percentage
const progressPercentage = 67; // 67% complete
// Component should show progress bar: 67%
});
});
describe('Admin Features', () => {
test('should show create course button for admins', () => {
// Test: create_course_works
const isAdmin = true;
if (isAdmin) {
// Component should show "+ Create Course" FAB
}
});
test('should show archive course button for course owners', () => {
// Test: archive_course_works
const course = generateMockCourse('Active', 0);
const isOwner = true;
if (isOwner) {
// Component should show "Archive" button in course menu
}
});
test('should prevent non-admins from creating courses', () => {
// Test: create_course_fails_for_non_admin
const isAdmin = false;
if (!isAdmin) {
// "Create Course" button should not be visible
}
});
});
describe('Pull to Refresh', () => {
test('should refresh course list on pull down', async () => {
const initialCourses = generateMockCourseList(5, 3);
// Simulate pull-to-refresh
// const { getByTestId } = render();
// fireEvent(getByTestId('course-list'), 'refresh');
// await waitFor(() => {
// expect(mockApi.query.perwerde.courses).toHaveBeenCalledTimes(2);
// });
});
});
describe('Empty States', () => {
test('should show empty state when no courses exist', () => {
const emptyCourses: any[] = [];
// Component should display:
// - Icon (📚)
// - Message: "No courses available yet"
// - Subtext: "Check back later for new courses"
});
test('should show empty state when no active courses', () => {
const courses = generateMockCourseList(5, 0); // All archived
const activeCourses = courses.filter(c => c.status === 'Active');
expect(activeCourses).toHaveLength(0);
// Component should display:
// - Message: "No active courses"
// - Button: "Show Archived Courses"
});
});
});
/**
* TEST DATA FIXTURES
*/
export const educationTestFixtures = {
activeCourse: generateMockCourse('Active', 0),
archivedCourse: generateMockCourse('Archived', 1),
courseList: generateMockCourseList(10, 7),
pendingEnrollment: generateMockEnrollment(0, false),
completedEnrollment: generateMockEnrollment(0, true),
categories: ['Blockchain', 'Programming', 'Kurdistan Culture', 'History', 'Languages'],
};