course-format-ts 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/.idea/edu-format-ts.iml +10 -0
  2. package/.idea/inspectionProfiles/Project_Default.xml +28 -0
  3. package/.idea/misc.xml +4 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/AGENTS.md +1 -0
  7. package/dist/courseFormat/AnswerPlaceholder.d.ts +37 -0
  8. package/dist/courseFormat/AnswerPlaceholder.js +101 -0
  9. package/dist/courseFormat/AnswerPlaceholderComparator.d.ts +4 -0
  10. package/dist/courseFormat/AnswerPlaceholderComparator.js +8 -0
  11. package/dist/courseFormat/AnswerPlaceholderDependency.d.ts +19 -0
  12. package/dist/courseFormat/AnswerPlaceholderDependency.js +91 -0
  13. package/dist/courseFormat/CheckFeedback.d.ts +20 -0
  14. package/dist/courseFormat/CheckFeedback.js +58 -0
  15. package/dist/courseFormat/CheckResult.d.ts +33 -0
  16. package/dist/courseFormat/CheckResult.js +58 -0
  17. package/dist/courseFormat/CheckResultSeverity.d.ts +7 -0
  18. package/dist/courseFormat/CheckResultSeverity.js +17 -0
  19. package/dist/courseFormat/CheckStatus.d.ts +5 -0
  20. package/dist/courseFormat/CheckStatus.js +9 -0
  21. package/dist/courseFormat/Course.d.ts +68 -0
  22. package/dist/courseFormat/Course.js +165 -0
  23. package/dist/courseFormat/CourseMode.d.ts +4 -0
  24. package/dist/courseFormat/CourseMode.js +8 -0
  25. package/dist/courseFormat/CourseVisibility.d.ts +4 -0
  26. package/dist/courseFormat/CourseVisibility.js +8 -0
  27. package/dist/courseFormat/CourseraCourse.d.ts +5 -0
  28. package/dist/courseFormat/CourseraCourse.js +15 -0
  29. package/dist/courseFormat/DescriptionFormat.d.ts +5 -0
  30. package/dist/courseFormat/DescriptionFormat.js +9 -0
  31. package/dist/courseFormat/EduCourse.d.ts +17 -0
  32. package/dist/courseFormat/EduCourse.js +42 -0
  33. package/dist/courseFormat/EduFile.d.ts +22 -0
  34. package/dist/courseFormat/EduFile.js +110 -0
  35. package/dist/courseFormat/EduFileErrorHighlightLevel.d.ts +5 -0
  36. package/dist/courseFormat/EduFileErrorHighlightLevel.js +9 -0
  37. package/dist/courseFormat/EduFormatNames.d.ts +75 -0
  38. package/dist/courseFormat/EduFormatNames.js +80 -0
  39. package/dist/courseFormat/EduTestInfo.d.ts +28 -0
  40. package/dist/courseFormat/EduTestInfo.js +68 -0
  41. package/dist/courseFormat/EduVersions.d.ts +2 -0
  42. package/dist/courseFormat/EduVersions.js +5 -0
  43. package/dist/courseFormat/FileContents.d.ts +36 -0
  44. package/dist/courseFormat/FileContents.js +67 -0
  45. package/dist/courseFormat/FileContentsFactory.d.ts +17 -0
  46. package/dist/courseFormat/FileContentsFactory.js +2 -0
  47. package/dist/courseFormat/FrameworkLesson.d.ts +9 -0
  48. package/dist/courseFormat/FrameworkLesson.js +28 -0
  49. package/dist/courseFormat/ItemContainer.d.ts +13 -0
  50. package/dist/courseFormat/ItemContainer.js +45 -0
  51. package/dist/courseFormat/JBAccountUserInfo.d.ts +9 -0
  52. package/dist/courseFormat/JBAccountUserInfo.js +20 -0
  53. package/dist/courseFormat/Language.d.ts +4 -0
  54. package/dist/courseFormat/Language.js +33 -0
  55. package/dist/courseFormat/Lesson.d.ts +20 -0
  56. package/dist/courseFormat/Lesson.js +56 -0
  57. package/dist/courseFormat/LessonContainer.d.ts +16 -0
  58. package/dist/courseFormat/LessonContainer.js +54 -0
  59. package/dist/courseFormat/PluginInfo.d.ts +7 -0
  60. package/dist/courseFormat/PluginInfo.js +15 -0
  61. package/dist/courseFormat/Section.d.ts +8 -0
  62. package/dist/courseFormat/Section.js +27 -0
  63. package/dist/courseFormat/StudyItem.d.ts +20 -0
  64. package/dist/courseFormat/StudyItem.js +47 -0
  65. package/dist/courseFormat/Tags.d.ts +16 -0
  66. package/dist/courseFormat/Tags.js +42 -0
  67. package/dist/courseFormat/TaskFile.d.ts +26 -0
  68. package/dist/courseFormat/TaskFile.js +72 -0
  69. package/dist/courseFormat/UserInfo.d.ts +3 -0
  70. package/dist/courseFormat/UserInfo.js +2 -0
  71. package/dist/courseFormat/Vendor.d.ts +7 -0
  72. package/dist/courseFormat/Vendor.js +14 -0
  73. package/dist/courseFormat/attempts/Attempt.d.ts +12 -0
  74. package/dist/courseFormat/attempts/Attempt.js +25 -0
  75. package/dist/courseFormat/attempts/AttemptBase.d.ts +9 -0
  76. package/dist/courseFormat/attempts/AttemptBase.js +28 -0
  77. package/dist/courseFormat/attempts/DataTaskAttempt.d.ts +6 -0
  78. package/dist/courseFormat/attempts/DataTaskAttempt.js +24 -0
  79. package/dist/courseFormat/attempts/Dataset.d.ts +12 -0
  80. package/dist/courseFormat/attempts/Dataset.js +21 -0
  81. package/dist/courseFormat/fileUtils.d.ts +5 -0
  82. package/dist/courseFormat/fileUtils.js +32 -0
  83. package/dist/courseFormat/hyperskill/HyperskillCourse.d.ts +13 -0
  84. package/dist/courseFormat/hyperskill/HyperskillCourse.js +25 -0
  85. package/dist/courseFormat/hyperskill/HyperskillProject.d.ts +10 -0
  86. package/dist/courseFormat/hyperskill/HyperskillProject.js +16 -0
  87. package/dist/courseFormat/hyperskill/HyperskillStage.d.ts +8 -0
  88. package/dist/courseFormat/hyperskill/HyperskillStage.js +20 -0
  89. package/dist/courseFormat/hyperskill/HyperskillTaskType.d.ts +4 -0
  90. package/dist/courseFormat/hyperskill/HyperskillTaskType.js +26 -0
  91. package/dist/courseFormat/hyperskill/HyperskillTopic.d.ts +5 -0
  92. package/dist/courseFormat/hyperskill/HyperskillTopic.js +11 -0
  93. package/dist/courseFormat/loggerUtils.d.ts +1 -0
  94. package/dist/courseFormat/loggerUtils.js +6 -0
  95. package/dist/courseFormat/stepik/StepikCourse.d.ts +5 -0
  96. package/dist/courseFormat/stepik/StepikCourse.js +15 -0
  97. package/dist/courseFormat/stepik/StepikLesson.d.ts +6 -0
  98. package/dist/courseFormat/stepik/StepikLesson.js +16 -0
  99. package/dist/courseFormat/tasks/AnswerTask.d.ts +8 -0
  100. package/dist/courseFormat/tasks/AnswerTask.js +11 -0
  101. package/dist/courseFormat/tasks/CodeTask.d.ts +12 -0
  102. package/dist/courseFormat/tasks/CodeTask.js +21 -0
  103. package/dist/courseFormat/tasks/DataTask.d.ts +18 -0
  104. package/dist/courseFormat/tasks/DataTask.js +32 -0
  105. package/dist/courseFormat/tasks/EduTask.d.ts +12 -0
  106. package/dist/courseFormat/tasks/EduTask.js +22 -0
  107. package/dist/courseFormat/tasks/IdeTask.d.ts +9 -0
  108. package/dist/courseFormat/tasks/IdeTask.js +14 -0
  109. package/dist/courseFormat/tasks/NumberTask.d.ts +9 -0
  110. package/dist/courseFormat/tasks/NumberTask.js +14 -0
  111. package/dist/courseFormat/tasks/OutputTask.d.ts +10 -0
  112. package/dist/courseFormat/tasks/OutputTask.js +18 -0
  113. package/dist/courseFormat/tasks/OutputTaskBase.d.ts +14 -0
  114. package/dist/courseFormat/tasks/OutputTaskBase.js +19 -0
  115. package/dist/courseFormat/tasks/RemoteEduTask.d.ts +9 -0
  116. package/dist/courseFormat/tasks/RemoteEduTask.js +15 -0
  117. package/dist/courseFormat/tasks/StringTask.d.ts +9 -0
  118. package/dist/courseFormat/tasks/StringTask.js +14 -0
  119. package/dist/courseFormat/tasks/TableTask.d.ts +17 -0
  120. package/dist/courseFormat/tasks/TableTask.js +43 -0
  121. package/dist/courseFormat/tasks/Task.d.ts +45 -0
  122. package/dist/courseFormat/tasks/Task.js +155 -0
  123. package/dist/courseFormat/tasks/TheoryTask.d.ts +10 -0
  124. package/dist/courseFormat/tasks/TheoryTask.js +15 -0
  125. package/dist/courseFormat/tasks/UnsupportedTask.d.ts +9 -0
  126. package/dist/courseFormat/tasks/UnsupportedTask.js +14 -0
  127. package/dist/courseFormat/tasks/choice/ChoiceOption.d.ts +10 -0
  128. package/dist/courseFormat/tasks/choice/ChoiceOption.js +33 -0
  129. package/dist/courseFormat/tasks/choice/ChoiceOptionStatus.d.ts +5 -0
  130. package/dist/courseFormat/tasks/choice/ChoiceOptionStatus.js +9 -0
  131. package/dist/courseFormat/tasks/choice/ChoiceTask.d.ts +23 -0
  132. package/dist/courseFormat/tasks/choice/ChoiceTask.js +47 -0
  133. package/dist/courseFormat/tasks/matching/MatchingTask.d.ts +10 -0
  134. package/dist/courseFormat/tasks/matching/MatchingTask.js +15 -0
  135. package/dist/courseFormat/tasks/matching/SortingBasedTask.d.ts +16 -0
  136. package/dist/courseFormat/tasks/matching/SortingBasedTask.js +50 -0
  137. package/dist/courseFormat/tasks/matching/SortingTask.d.ts +9 -0
  138. package/dist/courseFormat/tasks/matching/SortingTask.js +14 -0
  139. package/dist/courseFormat/uiMessages.d.ts +3 -0
  140. package/dist/courseFormat/uiMessages.js +14 -0
  141. package/dist/disk-loader.d.ts +4 -0
  142. package/dist/disk-loader.js +389 -0
  143. package/dist/index.d.ts +31 -0
  144. package/dist/index.js +64 -0
  145. package/dist/loader.d.ts +7 -0
  146. package/dist/loader.js +435 -0
  147. package/dist/models.d.ts +49 -0
  148. package/dist/models.js +2 -0
  149. package/dist/zip-loader.d.ts +4 -0
  150. package/dist/zip-loader.js +431 -0
  151. package/example-course-project/course-info.yaml +15 -0
  152. package/example-course-project/lesson1/lesson-info.yaml +3 -0
  153. package/example-course-project/lesson1/lesson-remote-info.yaml +1 -0
  154. package/example-course-project/lesson1/task1/Task.txt +1 -0
  155. package/example-course-project/lesson1/task1/task-info.yaml +12 -0
  156. package/example-course-project/lesson1/task1/task-remote-info.yaml +1 -0
  157. package/example-course-project/lesson1/task1/task.md +47 -0
  158. package/example-course-project/lesson1/task1/tests/Tests.txt +0 -0
  159. package/example-course-project/lesson1/task2/Task.txt +1 -0
  160. package/example-course-project/lesson1/task2/task-info.yaml +12 -0
  161. package/example-course-project/lesson1/task2/task-remote-info.yaml +1 -0
  162. package/example-course-project/lesson1/task2/task.md +47 -0
  163. package/example-course-project/lesson1/task2/tests/Tests.txt +0 -0
  164. package/package.json +19 -0
  165. package/src/@types/mime-types.d.ts +3 -0
  166. package/src/courseFormat/AnswerPlaceholder.ts +121 -0
  167. package/src/courseFormat/AnswerPlaceholderComparator.ts +7 -0
  168. package/src/courseFormat/AnswerPlaceholderDependency.ts +122 -0
  169. package/src/courseFormat/CheckFeedback.ts +71 -0
  170. package/src/courseFormat/CheckResult.ts +92 -0
  171. package/src/courseFormat/CheckResultSeverity.ts +13 -0
  172. package/src/courseFormat/CheckStatus.ts +5 -0
  173. package/src/courseFormat/Course.ts +201 -0
  174. package/src/courseFormat/CourseMode.ts +4 -0
  175. package/src/courseFormat/CourseVisibility.ts +4 -0
  176. package/src/courseFormat/CourseraCourse.ts +10 -0
  177. package/src/courseFormat/DescriptionFormat.ts +5 -0
  178. package/src/courseFormat/EduCourse.ts +41 -0
  179. package/src/courseFormat/EduFile.ts +133 -0
  180. package/src/courseFormat/EduFileErrorHighlightLevel.ts +5 -0
  181. package/src/courseFormat/EduFormatNames.ts +95 -0
  182. package/src/courseFormat/EduTestInfo.ts +87 -0
  183. package/src/courseFormat/EduVersions.ts +2 -0
  184. package/src/courseFormat/FileContents.ts +97 -0
  185. package/src/courseFormat/FileContentsFactory.ts +19 -0
  186. package/src/courseFormat/FrameworkLesson.ts +29 -0
  187. package/src/courseFormat/ItemContainer.ts +47 -0
  188. package/src/courseFormat/JBAccountUserInfo.ts +21 -0
  189. package/src/courseFormat/Language.ts +31 -0
  190. package/src/courseFormat/Lesson.ts +69 -0
  191. package/src/courseFormat/LessonContainer.ts +65 -0
  192. package/src/courseFormat/PluginInfo.ts +15 -0
  193. package/src/courseFormat/Section.ts +29 -0
  194. package/src/courseFormat/StudyItem.ts +55 -0
  195. package/src/courseFormat/Tags.ts +45 -0
  196. package/src/courseFormat/TaskFile.ts +88 -0
  197. package/src/courseFormat/UserInfo.ts +3 -0
  198. package/src/courseFormat/Vendor.ts +15 -0
  199. package/src/courseFormat/attempts/Attempt.ts +28 -0
  200. package/src/courseFormat/attempts/AttemptBase.ts +24 -0
  201. package/src/courseFormat/attempts/DataTaskAttempt.ts +19 -0
  202. package/src/courseFormat/attempts/Dataset.ts +13 -0
  203. package/src/courseFormat/fileUtils.ts +31 -0
  204. package/src/courseFormat/hyperskill/HyperskillCourse.ts +24 -0
  205. package/src/courseFormat/hyperskill/HyperskillProject.ts +10 -0
  206. package/src/courseFormat/hyperskill/HyperskillStage.ts +15 -0
  207. package/src/courseFormat/hyperskill/HyperskillTaskType.ts +23 -0
  208. package/src/courseFormat/hyperskill/HyperskillTopic.ts +5 -0
  209. package/src/courseFormat/loggerUtils.ts +3 -0
  210. package/src/courseFormat/stepik/StepikCourse.ts +10 -0
  211. package/src/courseFormat/stepik/StepikLesson.ts +11 -0
  212. package/src/courseFormat/tasks/AnswerTask.ts +13 -0
  213. package/src/courseFormat/tasks/CodeTask.ts +42 -0
  214. package/src/courseFormat/tasks/DataTask.ts +37 -0
  215. package/src/courseFormat/tasks/EduTask.ts +26 -0
  216. package/src/courseFormat/tasks/IdeTask.ts +17 -0
  217. package/src/courseFormat/tasks/NumberTask.ts +17 -0
  218. package/src/courseFormat/tasks/OutputTask.ts +21 -0
  219. package/src/courseFormat/tasks/OutputTaskBase.ts +23 -0
  220. package/src/courseFormat/tasks/RemoteEduTask.ts +18 -0
  221. package/src/courseFormat/tasks/StringTask.ts +17 -0
  222. package/src/courseFormat/tasks/TableTask.ts +51 -0
  223. package/src/courseFormat/tasks/Task.ts +181 -0
  224. package/src/courseFormat/tasks/TheoryTask.ts +19 -0
  225. package/src/courseFormat/tasks/UnsupportedTask.ts +17 -0
  226. package/src/courseFormat/tasks/choice/ChoiceOption.ts +37 -0
  227. package/src/courseFormat/tasks/choice/ChoiceOptionStatus.ts +5 -0
  228. package/src/courseFormat/tasks/choice/ChoiceTask.ts +57 -0
  229. package/src/courseFormat/tasks/matching/MatchingTask.ts +19 -0
  230. package/src/courseFormat/tasks/matching/SortingBasedTask.ts +59 -0
  231. package/src/courseFormat/tasks/matching/SortingTask.ts +17 -0
  232. package/src/courseFormat/uiMessages.ts +12 -0
  233. package/src/disk-loader.ts +463 -0
  234. package/src/index.ts +33 -0
  235. package/src/models.ts +54 -0
  236. package/src/zip-loader.ts +583 -0
  237. package/test/load-course.test.js +279 -0
  238. package/test/load-zip-course.test.js +73 -0
  239. package/tsconfig.json +15 -0
@@ -0,0 +1,279 @@
1
+ const path = require("node:path")
2
+ const assert = require("node:assert/strict")
3
+ const test = require("node:test")
4
+ const fs = require("node:fs")
5
+ const os = require("node:os")
6
+
7
+ const { loadCourseProject, EduCourse, EduTask, ChoiceTask, MatchingTask, SortingTask, TableTask } = require("../dist")
8
+
9
+ test("loads example course project into a Course object with correct class types", async () => {
10
+ const projectPath = path.join(__dirname, "..", "example-course-project")
11
+ const course = await loadCourseProject(projectPath)
12
+
13
+ // Course-level checks
14
+ assert.ok(course instanceof EduCourse)
15
+ assert.equal(course.title, "Example Plain Text course")
16
+ assert.equal(course.language, "English")
17
+ assert.equal(course.programmingLanguage, "Plain text")
18
+ assert.equal(course.courseMode, "STUDENT")
19
+ assert.equal(course.content.length, 1)
20
+
21
+ // Lesson-level checks
22
+ const lesson = course.content[0]
23
+ assert.equal(lesson.name, "lesson1")
24
+ assert.equal(lesson.content.length, 2)
25
+
26
+ // Task-level checks
27
+ const task1 = lesson.content[0]
28
+ assert.ok(task1 instanceof EduTask)
29
+ assert.equal(task1.name, "task1")
30
+ assert.equal(task1.files.length, 2)
31
+
32
+ const taskFile = task1.files.find((file) => file.name === "Task.txt")
33
+ assert.ok(taskFile)
34
+ assert.ok(taskFile.text && taskFile.text.includes("type your solution here"))
35
+ })
36
+
37
+ /**
38
+ * Helper: quickly build a mini YAML course project on disk.
39
+ */
40
+ function writeYamlProject(baseDir, structure) {
41
+ for (const [relPath, content] of Object.entries(structure)) {
42
+ const fullPath = path.join(baseDir, relPath)
43
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true })
44
+ fs.writeFileSync(fullPath, content, "utf8")
45
+ }
46
+ }
47
+
48
+ test("loads choice task with options from YAML", async () => {
49
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-choice-"))
50
+ try {
51
+ writeYamlProject(tmpDir, {
52
+ "course-info.yaml": `
53
+ title: Choice Test Course
54
+ language: English
55
+ programming_language: Python
56
+ mode: student
57
+ content:
58
+ - lesson1
59
+ `,
60
+ "lesson1/lesson-info.yaml": `
61
+ content:
62
+ - task1
63
+ `,
64
+ "lesson1/task1/task-info.yaml": `
65
+ type: choice
66
+ is_multiple_choice: true
67
+ options:
68
+ - text: Option A
69
+ is_correct: true
70
+ - text: Option B
71
+ is_correct: false
72
+ - text: Option C
73
+ is_correct: true
74
+ status: Unchecked
75
+ record: -1
76
+ `,
77
+ "lesson1/task1/Task.txt": "placeholder",
78
+ })
79
+
80
+ const course = await loadCourseProject(tmpDir)
81
+ const task = course.content[0].content[0]
82
+
83
+ assert.ok(task instanceof ChoiceTask, "Should be a ChoiceTask instance")
84
+ assert.equal(task.name, "task1")
85
+ assert.equal(task.isMultipleChoice, true)
86
+
87
+ const options = task.choiceOptions
88
+ assert.ok(options)
89
+ assert.equal(options.length, 3)
90
+ assert.equal(options[0].text, "Option A")
91
+ assert.equal(options[1].text, "Option B")
92
+ assert.equal(options[2].text, "Option C")
93
+ }
94
+ finally {
95
+ fs.rmSync(tmpDir, { recursive: true, force: true })
96
+ }
97
+ })
98
+
99
+ test("loads matching task with captions", async () => {
100
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-matching-"))
101
+ try {
102
+ writeYamlProject(tmpDir, {
103
+ "course-info.yaml": `
104
+ title: Matching Test Course
105
+ language: English
106
+ programming_language: Python
107
+ content:
108
+ - lesson1
109
+ `,
110
+ "lesson1/lesson-info.yaml": `
111
+ content:
112
+ - task1
113
+ `,
114
+ "lesson1/task1/task-info.yaml": `
115
+ type: matching
116
+ options:
117
+ - a
118
+ - b
119
+ - c
120
+ captions:
121
+ - first: "1"
122
+ second: "one"
123
+ - first: "2"
124
+ second: "two"
125
+ status: Unchecked
126
+ `,
127
+ })
128
+
129
+ const course = await loadCourseProject(tmpDir)
130
+ const task = course.content[0].content[0]
131
+
132
+ assert.ok(task instanceof MatchingTask, "Should be a MatchingTask instance")
133
+ assert.equal(task.name, "task1")
134
+ assert.deepEqual(task.options, ["a", "b", "c"])
135
+ assert.deepEqual(task.captions, ["1 : one", "2 : two"])
136
+ }
137
+ finally {
138
+ fs.rmSync(tmpDir, { recursive: true, force: true })
139
+ }
140
+ })
141
+
142
+ test("loads sorting task with options", async () => {
143
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-sorting-"))
144
+ try {
145
+ writeYamlProject(tmpDir, {
146
+ "course-info.yaml": `
147
+ title: Sorting Test Course
148
+ language: English
149
+ programming_language: Python
150
+ content:
151
+ - lesson1
152
+ `,
153
+ "lesson1/lesson-info.yaml": `
154
+ content:
155
+ - task1
156
+ `,
157
+ "lesson1/task1/task-info.yaml": `
158
+ type: sorting
159
+ options:
160
+ - first
161
+ - second
162
+ - third
163
+ status: Unchecked
164
+ `,
165
+ })
166
+
167
+ const course = await loadCourseProject(tmpDir)
168
+ const task = course.content[0].content[0]
169
+
170
+ assert.ok(task instanceof SortingTask, "Should be a SortingTask instance")
171
+ assert.deepEqual(task.options, ["first", "second", "third"])
172
+ }
173
+ finally {
174
+ fs.rmSync(tmpDir, { recursive: true, force: true })
175
+ }
176
+ })
177
+
178
+ test("loads table task with rows, columns, and selected cells", async () => {
179
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-table-"))
180
+ try {
181
+ writeYamlProject(tmpDir, {
182
+ "course-info.yaml": `
183
+ title: Table Test Course
184
+ language: English
185
+ programming_language: Python
186
+ content:
187
+ - lesson1
188
+ `,
189
+ "lesson1/lesson-info.yaml": `
190
+ content:
191
+ - task1
192
+ `,
193
+ "lesson1/task1/task-info.yaml": `
194
+ type: table
195
+ is_multiple_choice: true
196
+ rows:
197
+ - Row1
198
+ - Row2
199
+ columns:
200
+ - Col1
201
+ - Col2
202
+ selected:
203
+ - [0, 1]
204
+ - [1, 0]
205
+ status: Unchecked
206
+ `,
207
+ })
208
+
209
+ const course = await loadCourseProject(tmpDir)
210
+ const task = course.content[0].content[0]
211
+
212
+ assert.ok(task instanceof TableTask, "Should be a TableTask instance")
213
+ assert.equal(task.isMultipleChoice, true)
214
+ assert.deepEqual(task.rows, ["Row1", "Row2"])
215
+ assert.deepEqual(task.columns, ["Col1", "Col2"])
216
+ assert.deepEqual(task.selected, [[false, true], [true, false]])
217
+ }
218
+ finally {
219
+ fs.rmSync(tmpDir, { recursive: true, force: true })
220
+ }
221
+ })
222
+
223
+ test("creates correct task types for all supported task YAML types", async () => {
224
+ const typeClassMap = {
225
+ edu: "EduTask",
226
+ pycharm: "EduTask",
227
+ code: "CodeTask",
228
+ number: "NumberTask",
229
+ string: "StringTask",
230
+ output: "OutputTask",
231
+ dataset: "DataTask",
232
+ table: "TableTask",
233
+ theory: "TheoryTask",
234
+ ide: "IdeTask",
235
+ unsupported: "UnsupportedTask",
236
+ remote_edu: "RemoteEduTask",
237
+ choice: "ChoiceTask",
238
+ matching: "MatchingTask",
239
+ sorting: "SortingTask",
240
+ }
241
+
242
+ const { EduTask, CodeTask, NumberTask, StringTask, OutputTask, DataTask, TableTask, TheoryTask, IdeTask, UnsupportedTask, RemoteEduTask, ChoiceTask, MatchingTask, SortingTask } = require("../dist")
243
+
244
+ const classMap = { EduTask, CodeTask, NumberTask, StringTask, OutputTask, DataTask, TableTask, TheoryTask, IdeTask, UnsupportedTask, RemoteEduTask, ChoiceTask, MatchingTask, SortingTask }
245
+
246
+ for (const [type, expectedClassName] of Object.entries(typeClassMap)) {
247
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `test-${type}-`))
248
+ try {
249
+ writeYamlProject(tmpDir, {
250
+ "course-info.yaml": `
251
+ title: ${type} Test
252
+ language: English
253
+ programming_language: Python
254
+ content:
255
+ - lesson1
256
+ `,
257
+ "lesson1/lesson-info.yaml": `
258
+ content:
259
+ - task1
260
+ `,
261
+ "lesson1/task1/task-info.yaml": `
262
+ type: ${type}
263
+ status: Unchecked
264
+ record: -1
265
+ `,
266
+ "lesson1/task1/Task.txt": "test",
267
+ })
268
+
269
+ const course = await loadCourseProject(tmpDir)
270
+ const task = course.content[0].content[0]
271
+
272
+ const ExpectedClass = classMap[expectedClassName]
273
+ assert.ok(task instanceof ExpectedClass, `type "${type}" should produce ${expectedClassName}`)
274
+ }
275
+ finally {
276
+ fs.rmSync(tmpDir, { recursive: true, force: true })
277
+ }
278
+ }
279
+ })
@@ -0,0 +1,73 @@
1
+ const assert = require("node:assert/strict")
2
+ const test = require("node:test")
3
+
4
+ const {
5
+ loadCourseProjectFromZip,
6
+ EduCourse,
7
+ Section,
8
+ EduTask,
9
+ ChoiceTask,
10
+ } = require("../dist")
11
+
12
+ const ZIP_URL = "https://gitee.com/k88936/microsoft-vs-code-smell/releases/download/v0.4.0/Microsoft_V_S__Code__a_tour_about_refactor.zip"
13
+
14
+ test("loads a course from a zip URL into a Course object", async () => {
15
+ // Download the zip file
16
+ const response = await fetch(ZIP_URL)
17
+ if (!response.ok) {
18
+ throw new Error(`Failed to download zip: ${response.status} ${response.statusText}`)
19
+ }
20
+ const arrayBuffer = await response.arrayBuffer()
21
+ const zipData = new Uint8Array(arrayBuffer)
22
+
23
+ // Load the course from the zip
24
+ const course = await loadCourseProjectFromZip(zipData)
25
+
26
+ // Course-level checks
27
+ assert.ok(course instanceof EduCourse, "Course should be an EduCourse instance")
28
+ assert.equal(course.title, "Microsoft VS Code: a tour about refactor")
29
+ assert.equal(course.language, "en")
30
+ assert.equal(course.programmingLanguage, "Python")
31
+ assert.ok(course.vendor)
32
+ assert.equal(course.vendor.name, "k88936")
33
+
34
+ // Section-level checks
35
+ assert.ok(course.sections.length >= 2, "Course should have at least 2 sections")
36
+ const sectionNames = course.sections.map((s) => s.name)
37
+ assert.ok(sectionNames.includes("RefactoringAndItsPurpose"), "Should contain RefactoringAndItsPurpose section")
38
+ assert.ok(sectionNames.includes("Bloaters"), "Should contain Bloaters section")
39
+
40
+ // Check that a section has lessons
41
+ const firstSection = course.sections[0]
42
+ assert.ok(firstSection.items.length > 0, "Section should have lessons")
43
+ const lesson = firstSection.items[0]
44
+ assert.ok(lesson.name.length > 0, "Lesson should have a name")
45
+
46
+ // Check that a task has files loaded with text content
47
+ const allTasks = []
48
+ for (const section of course.sections) {
49
+ for (const lessonItem of section.items) {
50
+ for (const task of lessonItem.taskList) {
51
+ allTasks.push(task)
52
+ }
53
+ }
54
+ }
55
+
56
+ assert.ok(allTasks.length > 0, "Course should have tasks")
57
+
58
+ // Find a theory task and check it has file content
59
+ const theoryTask = allTasks.find((t) => t instanceof EduTask || t instanceof ChoiceTask)
60
+ if (theoryTask) {
61
+ assert.ok(theoryTask.files.length > 0, "Task should have files")
62
+ const taskFile = theoryTask.files[0]
63
+ assert.ok(taskFile.text && taskFile.text.length > 0, "Task file should have text content")
64
+ }
65
+
66
+ // Find a choice task and verify its options
67
+ const choiceTask = allTasks.find((t) => t instanceof ChoiceTask)
68
+ if (choiceTask) {
69
+ assert.ok(choiceTask.choiceOptions.length > 0, "Choice task should have options")
70
+ assert.equal(choiceTask.isMultipleChoice, false, "Quiz should be single choice")
71
+ assert.ok(choiceTask.messageIncorrect && choiceTask.messageIncorrect.length > 0, "Should have incorrect message")
72
+ }
73
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2016",
4
+ "module": "commonjs",
5
+ "esModuleInterop": true,
6
+ "forceConsistentCasingInFileNames": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "types": ["node"],
10
+ "rootDir": "src",
11
+ "outDir": "dist",
12
+ "declaration": true
13
+ },
14
+ "include": ["src"]
15
+ }