Skip to content

Commit 48a790a

Browse files
author
Rajat
committed
added tests for scorm
1 parent 563f8ee commit 48a790a

File tree

4 files changed

+426
-1
lines changed

4 files changed

+426
-1
lines changed

apps/web/app/(with-contexts)/dashboard/(sidebar)/product/[id]/content/section/[section]/lesson/__tests__/page.test.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ jest.mock("@courselit/components-library", () => ({
6464
useToast: () => ({
6565
toast: jest.fn(),
6666
}),
67+
Chip: ({ children }: any) => <div data-testid="chip">{children}</div>,
68+
useMediaLit: () => ({
69+
uploadFile: jest.fn(),
70+
isUploading: false,
71+
uploadProgress: 0,
72+
}),
6773
}));
6874

6975
jest.mock("../lesson-content-renderer", () => ({
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
import { markLessonCompleted } from "../logic";
2+
import LessonModel from "@/models/Lesson";
3+
import UserModel from "@/models/User";
4+
import CourseModel from "@/models/Course";
5+
import DomainModel from "@/models/Domain";
6+
import { Constants } from "@courselit/common-models";
7+
import mongoose from "mongoose";
8+
9+
const SUITE_PREFIX = `scorm-tests-${Date.now()}`;
10+
const id = (suffix: string) => `${SUITE_PREFIX}-${suffix}`;
11+
const email = (suffix: string) => `${suffix}-${SUITE_PREFIX}@example.com`;
12+
13+
describe("SCORM Logic Integration", () => {
14+
let testDomain: any;
15+
let user: any;
16+
let course: any;
17+
let scormLesson: any;
18+
let mockCtx: any;
19+
20+
beforeAll(async () => {
21+
// Create Domain
22+
testDomain = await DomainModel.create({
23+
name: id("domain"),
24+
email: email("domain"),
25+
features: [],
26+
});
27+
28+
// Create User
29+
user = await UserModel.create({
30+
domain: testDomain._id,
31+
userId: id("user"),
32+
email: email("user"),
33+
name: "Test User",
34+
active: true,
35+
permissions: [],
36+
unsubscribeToken: id("unsubscribe-user"),
37+
purchases: [],
38+
});
39+
40+
const groupId = new mongoose.Types.ObjectId().toString();
41+
42+
// Create Course
43+
course = await CourseModel.create({
44+
domain: testDomain._id,
45+
courseId: id("course"),
46+
title: "SCORM Course",
47+
lessons: [],
48+
creatorId: user.userId,
49+
cost: 0,
50+
privacy: "public",
51+
type: "course",
52+
costType: "free",
53+
slug: id("course-slug"),
54+
published: true,
55+
groups: [
56+
{
57+
_id: groupId,
58+
name: "Default Group",
59+
lessonsOrder: [],
60+
rank: 1,
61+
collapsed: true,
62+
drip: {
63+
status: false,
64+
type: "relative-date",
65+
},
66+
},
67+
],
68+
});
69+
70+
// Create SCORM Lesson
71+
scormLesson = await LessonModel.create({
72+
domain: testDomain._id,
73+
courseId: course.courseId,
74+
lessonId: id("lesson-scorm"),
75+
title: "SCORM Lesson",
76+
type: Constants.LessonType.SCORM,
77+
requiresEnrollment: true,
78+
content: {
79+
launchUrl: "index.html",
80+
version: "1.2",
81+
},
82+
creatorId: user.userId,
83+
groupId: groupId,
84+
});
85+
86+
// Add lesson to course
87+
course.lessons.push(scormLesson.lessonId);
88+
await course.save();
89+
90+
// Enroll user
91+
user.purchases.push({
92+
courseId: course.courseId,
93+
accessibleGroups: [groupId], // User needs access to the group too!
94+
completedLessons: [],
95+
scormData: {
96+
lessons: {},
97+
},
98+
});
99+
user.markModified("purchases");
100+
await user.save();
101+
102+
mockCtx = {
103+
user: user,
104+
subdomain: testDomain,
105+
} as any;
106+
});
107+
108+
afterAll(async () => {
109+
await UserModel.deleteMany({ domain: testDomain._id });
110+
await LessonModel.deleteMany({ domain: testDomain._id });
111+
await CourseModel.deleteMany({ domain: testDomain._id });
112+
await DomainModel.deleteOne({ _id: testDomain._id });
113+
});
114+
115+
beforeEach(async () => {
116+
// Reset user progress for the lesson
117+
const u = await UserModel.findById(user._id);
118+
const purchase = u!.purchases.find(
119+
(p: any) => p.courseId === course.courseId,
120+
);
121+
if (!purchase) throw new Error("Purchase not found");
122+
123+
purchase.completedLessons = [];
124+
ensureScormData(purchase);
125+
purchase.scormData.lessons = {}; // Clear lessons
126+
127+
u!.markModified("purchases");
128+
await u!.save();
129+
mockCtx.user = u; // update context user
130+
});
131+
132+
const ensureScormData = (purchase: any) => {
133+
if (!purchase.scormData) purchase.scormData = {};
134+
if (!purchase.scormData.lessons) purchase.scormData.lessons = {};
135+
return purchase;
136+
};
137+
138+
it("should fail validation if no SCORM data exists", async () => {
139+
await expect(
140+
markLessonCompleted(scormLesson.lessonId, mockCtx),
141+
).rejects.toThrow("Please complete the SCORM content first");
142+
});
143+
144+
it("should fail if SCORM 1.2 status is incomplete", async () => {
145+
// Update user with incomplete status
146+
const u = await UserModel.findById(user._id);
147+
const purchase = u!.purchases.find(
148+
(p: any) => p.courseId === course.courseId,
149+
);
150+
ensureScormData(purchase);
151+
152+
purchase.scormData.lessons[scormLesson.lessonId] = {
153+
cmi: {
154+
core: {
155+
lesson_status: "incomplete",
156+
},
157+
},
158+
};
159+
u!.markModified("purchases");
160+
await u!.save();
161+
mockCtx.user = u;
162+
163+
await expect(
164+
markLessonCompleted(scormLesson.lessonId, mockCtx),
165+
).rejects.toThrow("Please complete the SCORM content first");
166+
});
167+
168+
it("should succeed if SCORM 1.2 status is completed", async () => {
169+
// Update user with completed status
170+
const u = await UserModel.findById(user._id);
171+
const purchase = u!.purchases.find(
172+
(p: any) => p.courseId === course.courseId,
173+
);
174+
ensureScormData(purchase);
175+
176+
purchase.scormData.lessons[scormLesson.lessonId] = {
177+
cmi: {
178+
core: {
179+
lesson_status: "completed",
180+
},
181+
},
182+
};
183+
u!.markModified("purchases");
184+
await u!.save();
185+
mockCtx.user = u;
186+
187+
const result = await markLessonCompleted(scormLesson.lessonId, mockCtx);
188+
expect(result).toBe(true);
189+
190+
// Verify it was marked as completed in progress
191+
const updatedUser = await UserModel.findById(user._id);
192+
const p = updatedUser!.purchases.find(
193+
(p: any) => p.courseId === course.courseId,
194+
);
195+
expect(p?.completedLessons).toContain(scormLesson.lessonId);
196+
});
197+
198+
it("should succeed if SCORM 2004 completion_status is completed", async () => {
199+
const u = await UserModel.findById(user._id);
200+
const purchase = u!.purchases.find(
201+
(p: any) => p.courseId === course.courseId,
202+
);
203+
ensureScormData(purchase);
204+
205+
purchase.scormData.lessons[scormLesson.lessonId] = {
206+
cmi: {
207+
completion_status: "completed",
208+
},
209+
};
210+
u!.markModified("purchases");
211+
await u!.save();
212+
mockCtx.user = u;
213+
214+
const result = await markLessonCompleted(scormLesson.lessonId, mockCtx);
215+
expect(result).toBe(true);
216+
});
217+
218+
it("should succeed via fallback if interaction data exists", async () => {
219+
const u = await UserModel.findById(user._id);
220+
const purchase = u!.purchases.find(
221+
(p: any) => p.courseId === course.courseId,
222+
);
223+
ensureScormData(purchase);
224+
225+
purchase.scormData.lessons[scormLesson.lessonId] = {
226+
cmi: {
227+
core: {
228+
lesson_status: "incomplete",
229+
exit: "suspend", // Has exit data
230+
},
231+
},
232+
};
233+
u!.markModified("purchases");
234+
await u!.save();
235+
mockCtx.user = u;
236+
237+
const result = await markLessonCompleted(scormLesson.lessonId, mockCtx);
238+
expect(result).toBe(true);
239+
});
240+
});

apps/web/graphql/users/__tests__/delete-user.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jest.mock("../../communities/logic", () => ({
5151
deleteCommunityPosts: jest.fn().mockResolvedValue(true),
5252
}));
5353

54-
const DELETE_USER_SUITE_PREFIX = `delete-user-${Date.now()}`;
54+
const DELETE_USER_SUITE_PREFIX = `delete-user-${Date.now()}-${Math.floor(Math.random() * 100000)}`;
5555
const duId = (suffix: string) => `${DELETE_USER_SUITE_PREFIX}-${suffix}`;
5656
const duEmail = (suffix: string) =>
5757
`${suffix}-${DELETE_USER_SUITE_PREFIX}@example.com`;

0 commit comments

Comments
 (0)