scorm-again 1.7.1 → 2.1.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 (124) hide show
  1. package/.babelrc +18 -7
  2. package/.github/dependabot.yml +5 -0
  3. package/.github/workflows/main.yml +79 -0
  4. package/.github/workflows/stale.yml +14 -0
  5. package/.jsdoc.json +4 -5
  6. package/.mocharc.json +8 -0
  7. package/.run/{Mocha Unit Tests.run.xml → Mocha Unit Tests (watch).run.xml } +6 -3
  8. package/.run/Template Mocha.run.xml +17 -0
  9. package/CONTRIBUTING.md +1 -1
  10. package/README.md +183 -71
  11. package/dist/aicc.js +3822 -7030
  12. package/dist/aicc.js.map +1 -1
  13. package/dist/aicc.min.js +2 -40
  14. package/dist/aicc.min.js.map +1 -0
  15. package/dist/scorm-again.js +5965 -10498
  16. package/dist/scorm-again.js.map +1 -1
  17. package/dist/scorm-again.min.js +2 -52
  18. package/dist/scorm-again.min.js.map +1 -0
  19. package/dist/scorm12.js +3028 -5373
  20. package/dist/scorm12.js.map +1 -1
  21. package/dist/scorm12.min.js +2 -34
  22. package/dist/scorm12.min.js.map +1 -0
  23. package/dist/scorm2004.js +4054 -6693
  24. package/dist/scorm2004.js.map +1 -1
  25. package/dist/scorm2004.min.js +2 -40
  26. package/dist/scorm2004.min.js.map +1 -0
  27. package/eslint.config.js +21 -0
  28. package/package.json +76 -34
  29. package/results.json +34254 -0
  30. package/src/AICC.ts +72 -0
  31. package/src/BaseAPI.ts +1300 -0
  32. package/src/Scorm12API.ts +387 -0
  33. package/src/Scorm2004API.ts +688 -0
  34. package/src/cmi/aicc/attempts.ts +94 -0
  35. package/src/cmi/aicc/cmi.ts +100 -0
  36. package/src/cmi/aicc/core.ts +360 -0
  37. package/src/cmi/aicc/evaluation.ts +157 -0
  38. package/src/cmi/aicc/paths.ts +180 -0
  39. package/src/cmi/aicc/student_data.ts +86 -0
  40. package/src/cmi/aicc/student_demographics.ts +367 -0
  41. package/src/cmi/aicc/student_preferences.ts +176 -0
  42. package/src/cmi/aicc/tries.ts +116 -0
  43. package/src/cmi/aicc/validation.ts +25 -0
  44. package/src/cmi/common/array.ts +77 -0
  45. package/src/cmi/common/base_cmi.ts +46 -0
  46. package/src/cmi/common/score.ts +203 -0
  47. package/src/cmi/common/validation.ts +60 -0
  48. package/src/cmi/scorm12/cmi.ts +224 -0
  49. package/src/cmi/scorm12/interactions.ts +368 -0
  50. package/src/cmi/scorm12/nav.ts +54 -0
  51. package/src/cmi/scorm12/objectives.ts +112 -0
  52. package/src/cmi/scorm12/student_data.ts +130 -0
  53. package/src/cmi/scorm12/student_preference.ts +158 -0
  54. package/src/cmi/scorm12/validation.ts +48 -0
  55. package/src/cmi/scorm2004/adl.ts +272 -0
  56. package/src/cmi/scorm2004/cmi.ts +599 -0
  57. package/src/cmi/scorm2004/comments.ts +163 -0
  58. package/src/cmi/scorm2004/interactions.ts +466 -0
  59. package/src/cmi/scorm2004/learner_preference.ts +152 -0
  60. package/src/cmi/scorm2004/objectives.ts +212 -0
  61. package/src/cmi/scorm2004/score.ts +78 -0
  62. package/src/cmi/scorm2004/validation.ts +42 -0
  63. package/src/constants/api_constants.ts +318 -0
  64. package/src/constants/default_settings.ts +81 -0
  65. package/src/constants/enums.ts +5 -0
  66. package/src/constants/error_codes.ts +88 -0
  67. package/src/constants/language_constants.ts +394 -0
  68. package/src/constants/regex.ts +97 -0
  69. package/src/constants/{response_constants.js → response_constants.ts} +69 -62
  70. package/src/exceptions.ts +154 -0
  71. package/src/exports/aicc.js +1 -1
  72. package/src/exports/scorm-again.js +3 -3
  73. package/src/exports/scorm12.js +1 -1
  74. package/src/exports/scorm2004.js +1 -1
  75. package/src/helpers/scheduled_commit.ts +42 -0
  76. package/src/interfaces/IBaseAPI.ts +35 -0
  77. package/src/types/api_types.ts +32 -0
  78. package/src/utilities/debounce.ts +31 -0
  79. package/src/utilities.ts +338 -0
  80. package/tea.yaml +6 -0
  81. package/test/{AICC.spec.js → AICC.spec.ts} +79 -71
  82. package/test/Scorm12API.spec.ts +833 -0
  83. package/test/Scorm2004API.spec.ts +1298 -0
  84. package/test/api_helpers.ts +176 -0
  85. package/test/cmi/aicc_cmi.spec.ts +845 -0
  86. package/test/cmi/{scorm12_cmi.spec.js → scorm12_cmi.spec.ts} +253 -271
  87. package/test/cmi/scorm2004_cmi.spec.ts +1031 -0
  88. package/test/cmi_helpers.ts +207 -0
  89. package/test/exceptions.spec.ts +79 -0
  90. package/test/field_values.ts +202 -0
  91. package/test/types/api_types.spec.ts +126 -0
  92. package/test/utilities/debounce.spec.ts +56 -0
  93. package/test/utilities.spec.ts +322 -0
  94. package/tsconfig.json +18 -0
  95. package/webpack.config.js +65 -0
  96. package/.circleci/config.yml +0 -99
  97. package/.codeclimate.yml +0 -7
  98. package/.eslintrc.js +0 -36
  99. package/src/.flowconfig +0 -11
  100. package/src/AICC.js +0 -68
  101. package/src/BaseAPI.js +0 -1275
  102. package/src/Scorm12API.js +0 -308
  103. package/src/Scorm2004API.js +0 -572
  104. package/src/cmi/aicc_cmi.js +0 -1141
  105. package/src/cmi/common.js +0 -328
  106. package/src/cmi/scorm12_cmi.js +0 -1312
  107. package/src/cmi/scorm2004_cmi.js +0 -1692
  108. package/src/constants/api_constants.js +0 -218
  109. package/src/constants/error_codes.js +0 -87
  110. package/src/constants/language_constants.js +0 -76
  111. package/src/constants/regex.js +0 -84
  112. package/src/exceptions.js +0 -104
  113. package/src/utilities.js +0 -242
  114. package/test/Scorm12API.spec.js +0 -528
  115. package/test/Scorm2004API.spec.js +0 -775
  116. package/test/abstract_classes.spec.js +0 -17
  117. package/test/api_helpers.js +0 -128
  118. package/test/cmi/aicc_cmi.spec.js +0 -684
  119. package/test/cmi/scorm2004_cmi.spec.js +0 -1066
  120. package/test/cmi_helpers.js +0 -161
  121. package/test/exceptions.spec.js +0 -71
  122. package/test/field_values.js +0 -353
  123. package/test/utilities.spec.js +0 -339
  124. package/webpack.js +0 -78
@@ -0,0 +1,833 @@
1
+ import { expect } from "expect";
2
+ import { after, before, describe, it } from "mocha";
3
+ import Scorm12API from "../src/Scorm12API";
4
+ import * as h from "./api_helpers";
5
+ import ErrorCodes from "../src/constants/error_codes";
6
+ import { scorm12Values } from "./field_values";
7
+ import * as sinon from "sinon";
8
+ import Pretender from "fetch-pretender";
9
+ import { RefObject, Settings } from "../src/types/api_types";
10
+ import { DefaultSettings } from "../src/constants/default_settings";
11
+
12
+ const scorm12_error_codes = ErrorCodes.scorm12;
13
+
14
+ let clock: sinon.SinonFakeTimers;
15
+ const api = (settings?: Settings, startingData: RefObject = {}) => {
16
+ const API = new Scorm12API(settings);
17
+ API.apiLogLevel = 5;
18
+ API.startingData = startingData;
19
+ return API;
20
+ };
21
+ const apiInitialized = (settings?: Settings, startingData: RefObject = {}) => {
22
+ const API = api(settings);
23
+ API.loadFromJSON(startingData ? startingData : {}, "");
24
+ API.lmsInitialize();
25
+ return API;
26
+ };
27
+
28
+ describe("SCORM 1.2 API Tests", () => {
29
+ before(() => {
30
+ clock = sinon.useFakeTimers();
31
+
32
+ const server = new Pretender();
33
+ server.post(
34
+ "/scorm12",
35
+ () => {
36
+ return [200, { "Content-Type": "application/json" }, "{}"];
37
+ },
38
+ false,
39
+ );
40
+
41
+ server.post(
42
+ "/scorm12/error",
43
+ () => {
44
+ return [500, { "Content-Type": "application/json" }, "{}"];
45
+ },
46
+ false,
47
+ );
48
+ });
49
+
50
+ after(() => {
51
+ clock.restore();
52
+ });
53
+
54
+ describe("LMSSetValue()", () => {
55
+ h.checkValidValues({
56
+ api: apiInitialized(),
57
+ fieldName: "cmi.core.score.raw",
58
+ validValues: scorm12Values.validScoreRange,
59
+ invalidValues: scorm12Values.invalidScoreRange,
60
+ });
61
+ h.checkValidValues({
62
+ api: apiInitialized(),
63
+ fieldName: "cmi.core.score.min",
64
+ validValues: scorm12Values.validScoreRange,
65
+ invalidValues: scorm12Values.invalidScoreRange,
66
+ });
67
+ h.checkValidValues({
68
+ api: apiInitialized(),
69
+ fieldName: "cmi.core.score.max",
70
+ validValues: scorm12Values.validScoreRange,
71
+ invalidValues: scorm12Values.invalidScoreRange,
72
+ });
73
+ });
74
+
75
+ describe("setCMIValue()", () => {
76
+ describe("Invalid Sets - Should Always Fail", () => {
77
+ h.checkSetCMIValue({
78
+ api: api(),
79
+ fieldName: "cmi._version",
80
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
81
+ });
82
+ h.checkSetCMIValue({
83
+ api: api(),
84
+ fieldName: "cmi._children",
85
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
86
+ });
87
+ h.checkSetCMIValue({
88
+ api: api(),
89
+ fieldName: "cmi.core._children",
90
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
91
+ });
92
+ h.checkSetCMIValue({
93
+ api: api(),
94
+ fieldName: "cmi.core.score._children",
95
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
96
+ });
97
+ h.checkSetCMIValue({
98
+ api: api(),
99
+ fieldName: "cmi.objectives._children",
100
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
101
+ });
102
+ h.checkSetCMIValue({
103
+ api: api(),
104
+ fieldName: "cmi.objectives._count",
105
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
106
+ });
107
+ h.checkSetCMIValue({
108
+ api: api(),
109
+ fieldName: "cmi.interactions._children",
110
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
111
+ });
112
+ h.checkSetCMIValue({
113
+ api: api(),
114
+ fieldName: "cmi.interactions._count",
115
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
116
+ });
117
+ h.checkSetCMIValue({
118
+ api: api(),
119
+ fieldName: "cmi.interactions.0.objectives._count",
120
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
121
+ });
122
+ h.checkSetCMIValue({
123
+ api: api(),
124
+ fieldName: "cmi.interactions.0.correct_responses._count",
125
+ expectedError: scorm12_error_codes.INVALID_SET_VALUE,
126
+ });
127
+ });
128
+
129
+ describe("Invalid Sets - Should Fail After Initialization", () => {
130
+ h.checkSetCMIValue({
131
+ api: apiInitialized(),
132
+ fieldName: "cmi.launch_data",
133
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
134
+ });
135
+ h.checkSetCMIValue({
136
+ api: apiInitialized(),
137
+ fieldName: "cmi.comments_from_lms",
138
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
139
+ });
140
+ h.checkSetCMIValue({
141
+ api: apiInitialized(),
142
+ fieldName: "cmi.core.student_id",
143
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
144
+ });
145
+ h.checkSetCMIValue({
146
+ api: apiInitialized(),
147
+ fieldName: "cmi.core.student_name",
148
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
149
+ });
150
+ h.checkSetCMIValue({
151
+ api: apiInitialized(),
152
+ fieldName: "cmi.core.credit",
153
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
154
+ });
155
+ h.checkSetCMIValue({
156
+ api: apiInitialized(),
157
+ fieldName: "cmi.core.entry",
158
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
159
+ });
160
+ h.checkSetCMIValue({
161
+ api: apiInitialized(),
162
+ fieldName: "cmi.core.total_time",
163
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
164
+ });
165
+ h.checkSetCMIValue({
166
+ api: apiInitialized(),
167
+ fieldName: "cmi.core.lesson_mode",
168
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
169
+ });
170
+ h.checkSetCMIValue({
171
+ api: apiInitialized(),
172
+ fieldName: "cmi.student_data.mastery_score",
173
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
174
+ });
175
+ h.checkSetCMIValue({
176
+ api: apiInitialized(),
177
+ fieldName: "cmi.student_data.max_time_allowed",
178
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
179
+ });
180
+ h.checkSetCMIValue({
181
+ api: apiInitialized(),
182
+ fieldName: "cmi.student_data.time_limit_action",
183
+ expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
184
+ });
185
+ });
186
+ });
187
+
188
+ describe("LMSGetValue()", () => {
189
+ describe("Invalid Properties - Should Always Fail", () => {
190
+ h.checkLMSGetValue({
191
+ api: apiInitialized(),
192
+ fieldName: "cmi.core.close",
193
+ expectedError: scorm12_error_codes.GENERAL,
194
+ errorThrown: false,
195
+ });
196
+ h.checkLMSGetValue({
197
+ api: apiInitialized(),
198
+ fieldName: "cmi.exit",
199
+ expectedError: scorm12_error_codes.GENERAL,
200
+ errorThrown: false,
201
+ });
202
+ h.checkLMSGetValue({
203
+ api: apiInitialized(),
204
+ fieldName: "cmi.entry",
205
+ expectedError: scorm12_error_codes.GENERAL,
206
+ errorThrown: false,
207
+ });
208
+ });
209
+
210
+ describe("Read and Write Properties - Should Success", () => {
211
+ h.checkLMSGetValue({
212
+ api: apiInitialized(),
213
+ fieldName: "cmi.interactions.0.objectives.0.id",
214
+ initializeFirst: true,
215
+ initializationValue: "AAA",
216
+ expectedValue: "AAA",
217
+ });
218
+ });
219
+
220
+ describe("Write-Only Properties - Should Always Fail", () => {
221
+ h.checkLMSGetValue({
222
+ api: apiInitialized(),
223
+ fieldName: "cmi.core.exit",
224
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
225
+ });
226
+ h.checkLMSGetValue({
227
+ api: apiInitialized(),
228
+ fieldName: "cmi.core.session_time",
229
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
230
+ });
231
+ h.checkLMSGetValue({
232
+ api: apiInitialized(),
233
+ fieldName: "cmi.interactions.0.id",
234
+ initializeFirst: true,
235
+ initializationValue: "AAA",
236
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
237
+ });
238
+ h.checkLMSGetValue({
239
+ api: apiInitialized(),
240
+ fieldName: "cmi.interactions.0.time",
241
+ initializeFirst: true,
242
+ initializationValue: "12:59:59",
243
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
244
+ });
245
+ h.checkLMSGetValue({
246
+ api: apiInitialized(),
247
+ fieldName: "cmi.interactions.0.type",
248
+ initializeFirst: true,
249
+ initializationValue: "true-false",
250
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
251
+ });
252
+ h.checkLMSGetValue({
253
+ api: apiInitialized(),
254
+ fieldName: "cmi.interactions.0.weighting",
255
+ initializeFirst: true,
256
+ initializationValue: "0",
257
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
258
+ });
259
+ h.checkLMSGetValue({
260
+ api: apiInitialized(),
261
+ fieldName: "cmi.interactions.0.student_response",
262
+ initializeFirst: true,
263
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
264
+ });
265
+ h.checkLMSGetValue({
266
+ api: apiInitialized(),
267
+ fieldName: "cmi.interactions.0.result",
268
+ initializeFirst: true,
269
+ initializationValue: "correct",
270
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
271
+ });
272
+ h.checkLMSGetValue({
273
+ api: apiInitialized(),
274
+ fieldName: "cmi.interactions.0.latency",
275
+ initializeFirst: true,
276
+ initializationValue: "01:59:59.99",
277
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
278
+ });
279
+ h.checkLMSGetValue({
280
+ api: apiInitialized(),
281
+ fieldName: "cmi.interactions.0.correct_responses.0.pattern",
282
+ initializeFirst: true,
283
+ initializationValue: "AAA",
284
+ expectedValue: "AAA",
285
+ expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
286
+ });
287
+ });
288
+ });
289
+
290
+ describe("LMSSetValue()", () => {
291
+ describe("Uninitialized - Should Fail", () => {
292
+ h.checkLMSSetValue({
293
+ api: api(),
294
+ fieldName: "cmi.objectives.0.id",
295
+ expectedError: scorm12_error_codes.STORE_BEFORE_INIT,
296
+ });
297
+ h.checkLMSSetValue({
298
+ api: api(),
299
+ fieldName: "cmi.interactions.0.id",
300
+ expectedError: scorm12_error_codes.STORE_BEFORE_INIT,
301
+ });
302
+ });
303
+
304
+ describe("Initialized - Should Succeed", () => {
305
+ h.checkLMSSetValue({
306
+ api: apiInitialized(),
307
+ fieldName: "cmi.objectives.0.id",
308
+ valueToTest: "AAA",
309
+ });
310
+ h.checkLMSSetValue({
311
+ api: apiInitialized(),
312
+ fieldName: "cmi.interactions.0.id",
313
+ valueToTest: "AAA",
314
+ });
315
+ h.checkLMSSetValue({
316
+ api: apiInitialized(),
317
+ fieldName: "cmi.interactions.0.objectives.0.id",
318
+ valueToTest: "AAA",
319
+ });
320
+ h.checkLMSSetValue({
321
+ api: apiInitialized(),
322
+ fieldName: "cmi.interactions.10.correct_responses.0.pattern",
323
+ valueToTest: "t",
324
+ });
325
+ });
326
+ });
327
+
328
+ describe("replaceWithAnotherScormAPI()", () => {
329
+ it("should replace the current API with another API", () => {
330
+ const firstAPI = api();
331
+ const secondAPI = api();
332
+
333
+ firstAPI.cmi.core.student_id = "student_1";
334
+ secondAPI.cmi.core.student_id = "student_2";
335
+
336
+ firstAPI.replaceWithAnotherScormAPI(secondAPI);
337
+ expect(firstAPI.cmi.core.student_id).toEqual("student_2");
338
+ });
339
+ });
340
+
341
+ describe("renderCommitCMI()", () => {
342
+ it("should calculate total time when terminateCommit passed", () => {
343
+ const scorm12API = api();
344
+ scorm12API.cmi.core.total_time = "12:34:56";
345
+ scorm12API.cmi.core.session_time = "23:59:59";
346
+ const cmiExport: RefObject = scorm12API.renderCommitCMI(true);
347
+ expect(cmiExport.cmi.core.total_time).toEqual("36:34:55");
348
+ });
349
+ it("if the user passes, should calculate total time when terminateCommit passed", () => {
350
+ const scorm12API = api();
351
+ scorm12API.cmi.core.score.max = "100";
352
+ scorm12API.cmi.core.score.min = "0";
353
+ scorm12API.cmi.core.score.raw = "100";
354
+ scorm12API.cmi.core.exit = "suspend";
355
+ scorm12API.cmi.core.lesson_status = "completed";
356
+ scorm12API.cmi.core.total_time = "0000:00:00";
357
+ scorm12API.cmi.core.session_time = "23:59:59";
358
+ const cmiExport: RefObject = scorm12API.renderCommitCMI(true);
359
+ expect(cmiExport.cmi.core.total_time).toEqual("23:59:59");
360
+ });
361
+ });
362
+
363
+ describe("storeData()", () => {
364
+ it('should set cmi.core.lesson_status to "completed"', () => {
365
+ const scorm12API = api();
366
+ scorm12API.storeData(true);
367
+ expect(scorm12API.cmi.core.lesson_status).toEqual("completed");
368
+ });
369
+ it('should set cmi.core.lesson_status to "browsed"', () => {
370
+ const scorm12API = api();
371
+ scorm12API.cmi.core.lesson_mode = "browse";
372
+ scorm12API.storeData(true);
373
+ expect(scorm12API.cmi.core.lesson_status).toEqual("browsed");
374
+ });
375
+ it('should set cmi.core.lesson_status to "browsed" - Initial Status', () => {
376
+ const scorm12API = api();
377
+ scorm12API.startingData = { cmi: { core: { lesson_status: "" } } };
378
+ scorm12API.cmi.core.lesson_mode = "browse";
379
+ scorm12API.storeData(true);
380
+ expect(scorm12API.cmi.core.lesson_status).toEqual("browsed");
381
+ });
382
+ it('should set cmi.core.lesson_status to "passed" - mastery_override: true', () => {
383
+ const scorm12API = api({
384
+ ...DefaultSettings,
385
+ ...{ mastery_override: true },
386
+ });
387
+ scorm12API.cmi.core.credit = "credit";
388
+ scorm12API.cmi.student_data.mastery_score = "60.0";
389
+ scorm12API.cmi.core.score.raw = "75.0";
390
+ scorm12API.storeData(true);
391
+ expect(scorm12API.cmi.core.lesson_status).toEqual("passed");
392
+ });
393
+ it('should set cmi.core.lesson_status to "failed" - mastery_override: true', () => {
394
+ const scorm12API = api({
395
+ ...DefaultSettings,
396
+ ...{ mastery_override: true },
397
+ });
398
+ scorm12API.cmi.core.credit = "credit";
399
+ scorm12API.cmi.student_data.mastery_score = "60.0";
400
+ scorm12API.cmi.core.score.raw = "55.0";
401
+ scorm12API.storeData(true);
402
+ expect(scorm12API.cmi.core.lesson_status).toEqual("failed");
403
+ });
404
+ it('should set cmi.core.lesson_status to "passed" - mastery_override: false', () => {
405
+ const scorm12API = api({
406
+ ...DefaultSettings,
407
+ ...{ mastery_override: false },
408
+ });
409
+ scorm12API.cmi.core.lesson_status = "failed"; // module author wanted the user to pass, so we don't override
410
+ scorm12API.cmi.core.credit = "credit";
411
+ scorm12API.cmi.student_data.mastery_score = "60.0";
412
+ scorm12API.cmi.core.score.raw = "75.0";
413
+ scorm12API.storeData(true);
414
+ expect(scorm12API.cmi.core.lesson_status).toEqual("failed");
415
+ });
416
+ it('should set cmi.core.lesson_status to "failed" - mastery_override: false', () => {
417
+ const scorm12API = api({
418
+ ...DefaultSettings,
419
+ ...{ mastery_override: false },
420
+ });
421
+ scorm12API.cmi.core.lesson_status = "passed"; // module author wanted the user to pass, so we don't override
422
+ scorm12API.cmi.core.credit = "credit";
423
+ scorm12API.cmi.student_data.mastery_score = "60.0";
424
+ scorm12API.cmi.core.score.raw = "55.0";
425
+ scorm12API.storeData(true);
426
+ expect(scorm12API.cmi.core.lesson_status).toEqual("passed");
427
+ });
428
+ });
429
+
430
+ describe("Event Handlers", () => {
431
+ it("Should handle SetValue.cmi.core.student_name event", () => {
432
+ const scorm12API = apiInitialized();
433
+ const callback = sinon.spy();
434
+ scorm12API.on("LMSSetValue.cmi.core.student_name", callback);
435
+ scorm12API.lmsSetValue("cmi.core.student_name", "@jcputney");
436
+ expect(callback.called).toBe(true);
437
+ });
438
+ it("Should handle SetValue.cmi.* event", () => {
439
+ const scorm12API = apiInitialized();
440
+ const callback = sinon.spy();
441
+ scorm12API.on("LMSSetValue.cmi.*", callback);
442
+ scorm12API.lmsSetValue("cmi.core.student_name", "@jcputney");
443
+ expect(callback.called).toBe(true);
444
+ });
445
+ it("Should handle CommitSuccess event", async () => {
446
+ const scorm12API = api({
447
+ ...DefaultSettings,
448
+ ...{
449
+ lmsCommitUrl: "/scorm12",
450
+ autocommit: true,
451
+ autocommitSeconds: 1,
452
+ },
453
+ });
454
+ scorm12API.lmsInitialize();
455
+
456
+ const callback = sinon.spy();
457
+ scorm12API.on("CommitSuccess", callback);
458
+
459
+ scorm12API.lmsSetValue("cmi.core.session_time", "00:01:00");
460
+ clock.tick(2000);
461
+
462
+ await clock.runAllAsync();
463
+
464
+ expect(callback.called).toBe(true);
465
+ });
466
+ it("Should clear all event listeners for CommitSuccess", async () => {
467
+ const scorm12API = api({
468
+ ...DefaultSettings,
469
+ ...{
470
+ lmsCommitUrl: "/scorm12",
471
+ autocommit: true,
472
+ autocommitSeconds: 1,
473
+ },
474
+ });
475
+ scorm12API.lmsInitialize();
476
+
477
+ const callback = sinon.spy();
478
+ const callback2 = sinon.spy();
479
+ scorm12API.on("CommitSuccess", callback);
480
+ scorm12API.on("CommitSuccess", callback2);
481
+
482
+ scorm12API.lmsSetValue("cmi.core.session_time", "00:01:00");
483
+ clock.tick(2000);
484
+
485
+ await clock.runAllAsync();
486
+
487
+ expect(callback.calledOnce).toBe(true);
488
+ expect(callback2.calledOnce).toBe(true);
489
+
490
+ scorm12API.clear("CommitSuccess");
491
+
492
+ scorm12API.lmsSetValue("cmi.core.session_time", "00:01:00");
493
+ clock.tick(2000);
494
+
495
+ await clock.runAllAsync();
496
+
497
+ expect(callback.calledTwice).toBe(false);
498
+ expect(callback2.calledTwice).toBe(false);
499
+ });
500
+ it("Should clear only the specific event listener for CommitSuccess", async () => {
501
+ const scorm12API = api({
502
+ ...DefaultSettings,
503
+ ...{
504
+ lmsCommitUrl: "/scorm12",
505
+ autocommit: true,
506
+ autocommitSeconds: 1,
507
+ },
508
+ });
509
+ scorm12API.lmsInitialize();
510
+
511
+ const callback = sinon.spy(() => 1);
512
+ const callback2 = sinon.spy(() => 2);
513
+ const callback3 = sinon.spy(() => 3);
514
+ const callback4 = sinon.spy(() => 4);
515
+ scorm12API.on("CommitSuccess", callback);
516
+ scorm12API.on("CommitSuccess", callback2);
517
+ scorm12API.on("LMSCommit", callback3);
518
+ scorm12API.on("LMSSetValue", callback4);
519
+
520
+ scorm12API.lmsSetValue("cmi.core.session_time", "00:01:00");
521
+ clock.tick(2000);
522
+
523
+ await clock.runAllAsync();
524
+
525
+ expect(callback.calledOnce).toBe(true);
526
+ expect(callback2.calledOnce).toBe(true);
527
+ expect(callback3.calledOnce).toBe(true);
528
+ expect(callback4.calledOnce).toBe(true);
529
+
530
+ scorm12API.off("CommitSuccess", callback);
531
+
532
+ scorm12API.lmsSetValue("cmi.core.session_time", "00:01:00");
533
+ clock.tick(2000);
534
+
535
+ await clock.runAllAsync();
536
+
537
+ expect(callback.calledTwice).toBe(false); // removed callback should not be called a second time
538
+ expect(callback2.calledTwice).toBe(true);
539
+ expect(callback3.calledTwice).toBe(true);
540
+ expect(callback4.calledTwice).toBe(true);
541
+ });
542
+ it("Should handle CommitError event", async () => {
543
+ const scorm12API = api({
544
+ ...DefaultSettings,
545
+ ...{
546
+ lmsCommitUrl: "/scorm12/error",
547
+ autocommit: true,
548
+ autocommitSeconds: 1,
549
+ },
550
+ });
551
+ scorm12API.lmsInitialize();
552
+
553
+ const callback = sinon.spy();
554
+ scorm12API.on("CommitError", callback);
555
+
556
+ scorm12API.lmsSetValue("cmi.core.session_time", "00:01:00");
557
+ clock.tick(2000);
558
+
559
+ await clock.runAllAsync();
560
+
561
+ expect(callback.called).toBe(true);
562
+ });
563
+ it("Should handle CommitError event when offline", async () => {
564
+ const scorm2004API = api({
565
+ ...DefaultSettings,
566
+ ...{
567
+ lmsCommitUrl: "/scorm12/does_not_exist",
568
+ autocommit: true,
569
+ autocommitSeconds: 1,
570
+ },
571
+ });
572
+ scorm2004API.lmsInitialize();
573
+
574
+ const callback = sinon.spy();
575
+ scorm2004API.on("CommitError", callback);
576
+
577
+ scorm2004API.lmsSetValue("cmi.core.session_time", "00:01:00");
578
+ clock.tick(2000);
579
+
580
+ await clock.runAllAsync();
581
+
582
+ expect(callback.called).toBe(true);
583
+ });
584
+ });
585
+
586
+ describe("Test issues from users", () => {
587
+ it("Should be able to load the JSON data from issue #587", () => {
588
+ const scorm12api = api({
589
+ ...DefaultSettings,
590
+ autocommit: false,
591
+ lmsCommitUrl: "/scorm12",
592
+ dataCommitFormat: "flattened",
593
+ logLevel: 1,
594
+ });
595
+
596
+ scorm12api.loadFromFlattenedJSON(
597
+ {
598
+ "cmi.comments": "",
599
+ "cmi.comments_from_lms": "",
600
+ "cmi.core.credit": "",
601
+ "cmi.core.entry": "",
602
+ "cmi.core.exit": "",
603
+ "cmi.core.lesson_location": "topic-nOmtUy",
604
+ "cmi.core.lesson_mode": "normal",
605
+ "cmi.core.lesson_status": "completed",
606
+ "cmi.core.score.max": "100",
607
+ "cmi.core.score.min": "",
608
+ "cmi.core.score.raw": "",
609
+ "cmi.core.session_time": "00:03:50",
610
+ "cmi.core.student_id": "student1",
611
+ "cmi.core.student_name": "Student 1",
612
+ "cmi.core.total_time": "00:03:50",
613
+ "cmi.interactions": {},
614
+ "cmi.launch_data": "",
615
+ "cmi.objectives.0.id": "topic-aMMlFF",
616
+ "cmi.objectives.0.score.max": "100",
617
+ "cmi.objectives.0.score.min": "",
618
+ "cmi.objectives.0.score.raw": "",
619
+ "cmi.objectives.0.status": "completed",
620
+ "cmi.objectives.1.id": "topic-6ReD72",
621
+ "cmi.objectives.1.score.max": "100",
622
+ "cmi.objectives.1.score.min": "",
623
+ "cmi.objectives.1.score.raw": "",
624
+ "cmi.objectives.1.status": "completed",
625
+ "cmi.objectives.10.id": "topic-MllWvr",
626
+ "cmi.objectives.10.score.max": "100",
627
+ "cmi.objectives.10.score.min": "",
628
+ "cmi.objectives.10.score.raw": "",
629
+ "cmi.objectives.10.status": "completed",
630
+ "cmi.objectives.11.id": "topic-m0dnP3",
631
+ "cmi.objectives.11.score.max": "100",
632
+ "cmi.objectives.11.score.min": "",
633
+ "cmi.objectives.11.score.raw": "",
634
+ "cmi.objectives.11.status": "completed",
635
+ "cmi.objectives.12.id": "topic-so4hKC",
636
+ "cmi.objectives.12.score.max": "100",
637
+ "cmi.objectives.12.score.min": "",
638
+ "cmi.objectives.12.score.raw": "",
639
+ "cmi.objectives.12.status": "completed",
640
+ "cmi.objectives.13.id": "topic-S9p8FE",
641
+ "cmi.objectives.13.score.max": "100",
642
+ "cmi.objectives.13.score.min": "",
643
+ "cmi.objectives.13.score.raw": "",
644
+ "cmi.objectives.13.status": "completed",
645
+ "cmi.objectives.14.id": "topic-E97B5s",
646
+ "cmi.objectives.14.score.max": "100",
647
+ "cmi.objectives.14.score.min": "",
648
+ "cmi.objectives.14.score.raw": "",
649
+ "cmi.objectives.14.status": "completed",
650
+ "cmi.objectives.15.id": "topic-TTyEf0",
651
+ "cmi.objectives.15.score.max": "100",
652
+ "cmi.objectives.15.score.min": "",
653
+ "cmi.objectives.15.score.raw": "",
654
+ "cmi.objectives.15.status": "completed",
655
+ "cmi.objectives.16.id": "topic-Bk8ZLK",
656
+ "cmi.objectives.16.score.max": "100",
657
+ "cmi.objectives.16.score.min": "",
658
+ "cmi.objectives.16.score.raw": "",
659
+ "cmi.objectives.16.status": "completed",
660
+ "cmi.objectives.17.id": "topic-5rxzyV",
661
+ "cmi.objectives.17.score.max": "100",
662
+ "cmi.objectives.17.score.min": "",
663
+ "cmi.objectives.17.score.raw": "",
664
+ "cmi.objectives.17.status": "completed",
665
+ "cmi.objectives.18.id": "topic-Q3rjln",
666
+ "cmi.objectives.18.score.max": "100",
667
+ "cmi.objectives.18.score.min": "",
668
+ "cmi.objectives.18.score.raw": "",
669
+ "cmi.objectives.18.status": "completed",
670
+ "cmi.objectives.19.id": "topic-eIbwi4",
671
+ "cmi.objectives.19.score.max": "100",
672
+ "cmi.objectives.19.score.min": "",
673
+ "cmi.objectives.19.score.raw": "",
674
+ "cmi.objectives.19.status": "completed",
675
+ "cmi.objectives.2.id": "topic-sfvmuO",
676
+ "cmi.objectives.2.score.max": "100",
677
+ "cmi.objectives.2.score.min": "",
678
+ "cmi.objectives.2.score.raw": "",
679
+ "cmi.objectives.2.status": "completed",
680
+ "cmi.objectives.20.id": "topic-AzAspy",
681
+ "cmi.objectives.20.score.max": "100",
682
+ "cmi.objectives.20.score.min": "",
683
+ "cmi.objectives.20.score.raw": "",
684
+ "cmi.objectives.20.status": "completed",
685
+ "cmi.objectives.21.id": "topic-ehMV4A",
686
+ "cmi.objectives.21.score.max": "100",
687
+ "cmi.objectives.21.score.min": "",
688
+ "cmi.objectives.21.score.raw": "",
689
+ "cmi.objectives.21.status": "completed",
690
+ "cmi.objectives.22.id": "topic-U52hDp",
691
+ "cmi.objectives.22.score.max": "100",
692
+ "cmi.objectives.22.score.min": "",
693
+ "cmi.objectives.22.score.raw": "",
694
+ "cmi.objectives.22.status": "completed",
695
+ "cmi.objectives.23.id": "topic-mmAPuC",
696
+ "cmi.objectives.23.score.max": "100",
697
+ "cmi.objectives.23.score.min": "",
698
+ "cmi.objectives.23.score.raw": "",
699
+ "cmi.objectives.23.status": "completed",
700
+ "cmi.objectives.24.id": "topic-UyqqeN",
701
+ "cmi.objectives.24.score.max": "100",
702
+ "cmi.objectives.24.score.min": "",
703
+ "cmi.objectives.24.score.raw": "",
704
+ "cmi.objectives.24.status": "completed",
705
+ "cmi.objectives.25.id": "topic-UIALBn",
706
+ "cmi.objectives.25.score.max": "100",
707
+ "cmi.objectives.25.score.min": "",
708
+ "cmi.objectives.25.score.raw": "",
709
+ "cmi.objectives.25.status": "completed",
710
+ "cmi.objectives.26.id": "topic-oLGCQE",
711
+ "cmi.objectives.26.score.max": "100",
712
+ "cmi.objectives.26.score.min": "",
713
+ "cmi.objectives.26.score.raw": "",
714
+ "cmi.objectives.26.status": "completed",
715
+ "cmi.objectives.27.id": "topic-JhRmY0",
716
+ "cmi.objectives.27.score.max": "100",
717
+ "cmi.objectives.27.score.min": "",
718
+ "cmi.objectives.27.score.raw": "",
719
+ "cmi.objectives.27.status": "completed",
720
+ "cmi.objectives.28.id": "topic-677fLH",
721
+ "cmi.objectives.28.score.max": "100",
722
+ "cmi.objectives.28.score.min": "",
723
+ "cmi.objectives.28.score.raw": "",
724
+ "cmi.objectives.28.status": "completed",
725
+ "cmi.objectives.29.id": "topic-rKvnIL",
726
+ "cmi.objectives.29.score.max": "100",
727
+ "cmi.objectives.29.score.min": "",
728
+ "cmi.objectives.29.score.raw": "",
729
+ "cmi.objectives.29.status": "completed",
730
+ "cmi.objectives.3.id": "topic-IObzTr",
731
+ "cmi.objectives.3.score.max": "100",
732
+ "cmi.objectives.3.score.min": "",
733
+ "cmi.objectives.3.score.raw": "",
734
+ "cmi.objectives.3.status": "completed",
735
+ "cmi.objectives.30.id": "topic-Vjd6mO",
736
+ "cmi.objectives.30.score.max": "100",
737
+ "cmi.objectives.30.score.min": "",
738
+ "cmi.objectives.30.score.raw": "",
739
+ "cmi.objectives.30.status": "completed",
740
+ "cmi.objectives.31.id": "topic-jUsEtX",
741
+ "cmi.objectives.31.score.max": "100",
742
+ "cmi.objectives.31.score.min": "",
743
+ "cmi.objectives.31.score.raw": "",
744
+ "cmi.objectives.31.status": "completed",
745
+ "cmi.objectives.32.id": "topic-SvCWWr",
746
+ "cmi.objectives.32.score.max": "100",
747
+ "cmi.objectives.32.score.min": "",
748
+ "cmi.objectives.32.score.raw": "",
749
+ "cmi.objectives.32.status": "completed",
750
+ "cmi.objectives.33.id": "topic-knRFfG",
751
+ "cmi.objectives.33.score.max": "100",
752
+ "cmi.objectives.33.score.min": "",
753
+ "cmi.objectives.33.score.raw": "",
754
+ "cmi.objectives.33.status": "completed",
755
+ "cmi.objectives.34.id": "topic-wlFwhf",
756
+ "cmi.objectives.34.score.max": "100",
757
+ "cmi.objectives.34.score.min": "",
758
+ "cmi.objectives.34.score.raw": "",
759
+ "cmi.objectives.34.status": "completed",
760
+ "cmi.objectives.35.id": "topic-3b86fq",
761
+ "cmi.objectives.35.score.max": "100",
762
+ "cmi.objectives.35.score.min": "",
763
+ "cmi.objectives.35.score.raw": "",
764
+ "cmi.objectives.35.status": "completed",
765
+ "cmi.objectives.36.id": "topic-ZjIFKf",
766
+ "cmi.objectives.36.score.max": "100",
767
+ "cmi.objectives.36.score.min": "",
768
+ "cmi.objectives.36.score.raw": "",
769
+ "cmi.objectives.36.status": "completed",
770
+ "cmi.objectives.37.id": "topic-A9spZz",
771
+ "cmi.objectives.37.score.max": "100",
772
+ "cmi.objectives.37.score.min": "",
773
+ "cmi.objectives.37.score.raw": "",
774
+ "cmi.objectives.37.status": "completed",
775
+ "cmi.objectives.38.id": "topic-nOmtUy",
776
+ "cmi.objectives.38.score.max": "100",
777
+ "cmi.objectives.38.score.min": "",
778
+ "cmi.objectives.38.score.raw": "",
779
+ "cmi.objectives.38.status": "completed",
780
+ "cmi.objectives.39.id": "topic-jxtabl",
781
+ "cmi.objectives.39.score.max": "100",
782
+ "cmi.objectives.39.score.min": "",
783
+ "cmi.objectives.39.score.raw": "",
784
+ "cmi.objectives.39.status": "completed",
785
+ "cmi.objectives.4.id": "topic-Bq3O5v",
786
+ "cmi.objectives.4.score.max": "100",
787
+ "cmi.objectives.4.score.min": "",
788
+ "cmi.objectives.4.score.raw": "",
789
+ "cmi.objectives.4.status": "completed",
790
+ "cmi.objectives.5.id": "topic-HmnDxg",
791
+ "cmi.objectives.5.score.max": "100",
792
+ "cmi.objectives.5.score.min": "",
793
+ "cmi.objectives.5.score.raw": "",
794
+ "cmi.objectives.5.status": "completed",
795
+ "cmi.objectives.6.id": "topic-4YswmY",
796
+ "cmi.objectives.6.score.max": "100",
797
+ "cmi.objectives.6.score.min": "",
798
+ "cmi.objectives.6.score.raw": "",
799
+ "cmi.objectives.6.status": "completed",
800
+ "cmi.objectives.7.id": "topic-LuAS5e",
801
+ "cmi.objectives.7.score.max": "100",
802
+ "cmi.objectives.7.score.min": "",
803
+ "cmi.objectives.7.score.raw": "",
804
+ "cmi.objectives.7.status": "completed",
805
+ "cmi.objectives.8.id": "topic-K6ECXa",
806
+ "cmi.objectives.8.score.max": "100",
807
+ "cmi.objectives.8.score.min": "",
808
+ "cmi.objectives.8.score.raw": "",
809
+ "cmi.objectives.8.status": "completed",
810
+ "cmi.objectives.9.id": "topic-2lpxvN",
811
+ "cmi.objectives.9.score.max": "100",
812
+ "cmi.objectives.9.score.min": "",
813
+ "cmi.objectives.9.score.raw": "",
814
+ "cmi.objectives.9.status": "completed",
815
+ "cmi.student_data.mastery_score": "",
816
+ "cmi.student_data.max_time_allowed": "",
817
+ "cmi.student_data.time_limit_action": "",
818
+ "cmi.student_preference.audio": "",
819
+ "cmi.student_preference.language": "",
820
+ "cmi.student_preference.speed": "",
821
+ "cmi.student_preference.text": "",
822
+ "cmi.suspend_data": "",
823
+ },
824
+ "",
825
+ );
826
+ scorm12api.lmsInitialize();
827
+
828
+ expect(scorm12api.getCMIValue("cmi.objectives.10.id")).toEqual(
829
+ "topic-MllWvr",
830
+ );
831
+ });
832
+ });
833
+ });