scorm-again 2.0.0 → 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 (77) hide show
  1. package/.github/workflows/stale.yml +14 -0
  2. package/.run/{Mocha Unit Tests.run.xml → Mocha Unit Tests (watch).run.xml } +1 -1
  3. package/.run/Template Mocha.run.xml +17 -0
  4. package/README.md +171 -72
  5. package/dist/aicc.js +1441 -1140
  6. package/dist/aicc.js.map +1 -1
  7. package/dist/aicc.min.js +1 -1
  8. package/dist/aicc.min.js.map +1 -1
  9. package/dist/scorm-again.js +2703 -2212
  10. package/dist/scorm-again.js.map +1 -1
  11. package/dist/scorm-again.min.js +1 -1
  12. package/dist/scorm-again.min.js.map +1 -1
  13. package/dist/scorm12.js +1069 -852
  14. package/dist/scorm12.js.map +1 -1
  15. package/dist/scorm12.min.js +1 -1
  16. package/dist/scorm12.min.js.map +1 -1
  17. package/dist/scorm2004.js +1861 -1571
  18. package/dist/scorm2004.js.map +1 -1
  19. package/dist/scorm2004.min.js +1 -1
  20. package/dist/scorm2004.min.js.map +1 -1
  21. package/package.json +10 -6
  22. package/src/AICC.ts +15 -17
  23. package/src/BaseAPI.ts +268 -417
  24. package/src/Scorm12API.ts +65 -38
  25. package/src/Scorm2004API.ts +151 -117
  26. package/src/cmi/aicc/attempts.ts +94 -0
  27. package/src/cmi/aicc/cmi.ts +100 -0
  28. package/src/cmi/aicc/core.ts +360 -0
  29. package/src/cmi/aicc/evaluation.ts +157 -0
  30. package/src/cmi/aicc/paths.ts +180 -0
  31. package/src/cmi/aicc/student_data.ts +86 -0
  32. package/src/cmi/aicc/student_demographics.ts +367 -0
  33. package/src/cmi/aicc/student_preferences.ts +176 -0
  34. package/src/cmi/aicc/tries.ts +116 -0
  35. package/src/cmi/aicc/validation.ts +25 -0
  36. package/src/cmi/common/array.ts +77 -0
  37. package/src/cmi/common/base_cmi.ts +46 -0
  38. package/src/cmi/common/score.ts +203 -0
  39. package/src/cmi/common/validation.ts +60 -0
  40. package/src/cmi/scorm12/cmi.ts +224 -0
  41. package/src/cmi/scorm12/interactions.ts +368 -0
  42. package/src/cmi/scorm12/nav.ts +54 -0
  43. package/src/cmi/scorm12/objectives.ts +112 -0
  44. package/src/cmi/scorm12/student_data.ts +130 -0
  45. package/src/cmi/scorm12/student_preference.ts +158 -0
  46. package/src/cmi/scorm12/validation.ts +48 -0
  47. package/src/cmi/scorm2004/adl.ts +272 -0
  48. package/src/cmi/scorm2004/cmi.ts +599 -0
  49. package/src/cmi/scorm2004/comments.ts +163 -0
  50. package/src/cmi/scorm2004/interactions.ts +466 -0
  51. package/src/cmi/scorm2004/learner_preference.ts +152 -0
  52. package/src/cmi/scorm2004/objectives.ts +212 -0
  53. package/src/cmi/scorm2004/score.ts +78 -0
  54. package/src/cmi/scorm2004/validation.ts +42 -0
  55. package/src/constants/default_settings.ts +81 -0
  56. package/src/constants/enums.ts +5 -0
  57. package/src/constants/regex.ts +2 -2
  58. package/src/constants/response_constants.ts +2 -0
  59. package/src/exceptions.ts +22 -1
  60. package/src/helpers/scheduled_commit.ts +42 -0
  61. package/src/interfaces/IBaseAPI.ts +35 -0
  62. package/src/types/api_types.ts +32 -0
  63. package/src/utilities/debounce.ts +31 -0
  64. package/src/utilities.ts +56 -0
  65. package/test/AICC.spec.ts +11 -1
  66. package/test/Scorm12API.spec.ts +262 -9
  67. package/test/Scorm2004API.spec.ts +488 -2
  68. package/test/cmi/aicc_cmi.spec.ts +188 -11
  69. package/test/cmi/scorm12_cmi.spec.ts +5 -5
  70. package/test/cmi/scorm2004_cmi.spec.ts +8 -8
  71. package/test/cmi_helpers.ts +1 -1
  72. package/test/types/api_types.spec.ts +126 -0
  73. package/test/utilities/debounce.spec.ts +56 -0
  74. package/src/cmi/aicc_cmi.ts +0 -1248
  75. package/src/cmi/common.ts +0 -411
  76. package/src/cmi/scorm12_cmi.ts +0 -1426
  77. package/src/cmi/scorm2004_cmi.ts +0 -1874
@@ -1,12 +1,15 @@
1
1
  import { expect } from "expect";
2
- import { describe, it, before, after } from "mocha";
2
+ import { after, before, describe, it } from "mocha";
3
3
  import * as sinon from "sinon";
4
4
  import * as h from "./api_helpers";
5
5
  import Pretender from "fetch-pretender";
6
6
  import ErrorCodes from "../src/constants/error_codes";
7
7
  import Scorm2004API from "../src/Scorm2004API";
8
8
  import { scorm2004Values } from "./field_values";
9
- import { DefaultSettings, RefObject, Settings } from "../src/BaseAPI";
9
+ import APIConstants from "../src/constants/api_constants";
10
+ import { RefObject, Settings } from "../src/types/api_types";
11
+ import { DefaultSettings } from "../src/constants/default_settings";
12
+ import { CMI } from "../src/cmi/scorm2004/cmi";
10
13
 
11
14
  const scorm2004_error_codes = ErrorCodes.scorm2004;
12
15
 
@@ -27,6 +30,9 @@ const apiInitialized = (startingData?: RefObject) => {
27
30
  };
28
31
 
29
32
  describe("SCORM 2004 API Tests", () => {
33
+ let terminateStub: sinon.SinonStub;
34
+ let processListenersSpy: sinon.SinonSpy;
35
+
30
36
  before(() => {
31
37
  clock = sinon.useFakeTimers();
32
38
 
@@ -581,6 +587,158 @@ describe("SCORM 2004 API Tests", () => {
581
587
  });
582
588
  });
583
589
 
590
+ describe("reset()", () => {
591
+ it("should reset all CMI values to their default state", () => {
592
+ const scorm2004API = api();
593
+ scorm2004API.cmi.learner_id = "student_1";
594
+ scorm2004API.cmi.session_time = "PT1H0M0S";
595
+
596
+ scorm2004API.reset();
597
+
598
+ expect(scorm2004API.cmi).toEqual(new CMI());
599
+ });
600
+
601
+ it("should keep original settings", () => {
602
+ const scorm2004API = api({
603
+ dataCommitFormat: "flattened",
604
+ autocommit: true,
605
+ });
606
+
607
+ scorm2004API.reset();
608
+
609
+ expect(scorm2004API.settings.sendFullCommit).toEqual(
610
+ DefaultSettings.sendFullCommit,
611
+ );
612
+ expect(scorm2004API.settings.dataCommitFormat).toEqual("flattened");
613
+ expect(scorm2004API.settings.autocommit).toEqual(true);
614
+ });
615
+
616
+ it("should be able to override original settings", () => {
617
+ const scorm2004API = api({
618
+ ...DefaultSettings,
619
+ dataCommitFormat: "flattened",
620
+ autocommit: true,
621
+ });
622
+
623
+ scorm2004API.reset({
624
+ alwaysSendTotalTime: !DefaultSettings.alwaysSendTotalTime,
625
+ });
626
+
627
+ expect(scorm2004API.settings.sendFullCommit).toEqual(
628
+ DefaultSettings.sendFullCommit,
629
+ );
630
+ expect(scorm2004API.settings.dataCommitFormat).toEqual("flattened");
631
+ expect(scorm2004API.settings.autocommit).toEqual(true);
632
+ expect(scorm2004API.settings.alwaysSendTotalTime).toEqual(
633
+ !DefaultSettings.alwaysSendTotalTime,
634
+ );
635
+ });
636
+
637
+ it("should call commonReset from the superclass", () => {
638
+ const scorm2004API = api();
639
+ const commonResetSpy = sinon.spy(scorm2004API, "commonReset");
640
+
641
+ scorm2004API.reset();
642
+
643
+ expect(commonResetSpy.calledOnce).toBe(true);
644
+ commonResetSpy.restore();
645
+ });
646
+ });
647
+
648
+ describe("Scorm2004API.Finish", () => {
649
+ let scorm2004API = api();
650
+
651
+ beforeEach(() => {
652
+ scorm2004API = api();
653
+ terminateStub = sinon.stub(scorm2004API, "terminate");
654
+ processListenersSpy = sinon.spy(scorm2004API, "processListeners");
655
+ });
656
+
657
+ afterEach(() => {
658
+ terminateStub.restore();
659
+ processListenersSpy.restore();
660
+ });
661
+
662
+ describe("lmsFinish()", () => {
663
+ it("should call internalFinish and return SCORM_TRUE", async () => {
664
+ const internalFinishStub = sinon
665
+ .stub(scorm2004API, "internalFinish")
666
+ .resolves(APIConstants.global.SCORM_TRUE);
667
+ const result = scorm2004API.lmsFinish();
668
+ expect(result).toEqual(APIConstants.global.SCORM_TRUE);
669
+ expect(internalFinishStub.calledOnce).toBe(true);
670
+ internalFinishStub.restore();
671
+ });
672
+ });
673
+
674
+ describe("internalFinish()", () => {
675
+ const navActions: { [key: string]: string } = {
676
+ previous: "SequencePrevious",
677
+ continue: "SequenceNext",
678
+ choice: "SequenceChoice",
679
+ jump: "SequenceJump",
680
+ "{target=next-sco}choice": "SequenceChoice",
681
+ exit: "SequenceExit",
682
+ exitAll: "SequenceExitAll",
683
+ abandon: "SequenceAbandon",
684
+ abandonAll: "SequenceAbandonAll",
685
+ };
686
+
687
+ it("should call terminate with 'Terminate' and true", async () => {
688
+ terminateStub.resolves(APIConstants.global.SCORM_TRUE);
689
+ await scorm2004API.internalFinish();
690
+ expect(terminateStub.calledWith("Terminate", true)).toBe(true);
691
+ });
692
+
693
+ for (const navRequest of Object.keys(navActions)) {
694
+ it(`should process the correct navigation action based on adl.nav.request = ${navRequest}`, async () => {
695
+ terminateStub.resolves(APIConstants.global.SCORM_TRUE);
696
+ scorm2004API.adl.nav.request = navRequest;
697
+ await scorm2004API.internalFinish();
698
+ expect(processListenersSpy.calledWith(navActions[navRequest])).toBe(
699
+ true,
700
+ );
701
+ });
702
+ }
703
+
704
+ it("should process 'SequenceNext' if adl.nav.request is '_none_' and autoProgress is true", async () => {
705
+ terminateStub.resolves(APIConstants.global.SCORM_TRUE);
706
+ scorm2004API.adl.nav.request = "_none_";
707
+ scorm2004API.settings.autoProgress = true;
708
+ await scorm2004API.internalFinish();
709
+ expect(processListenersSpy.calledWith("SequenceNext")).toBe(true);
710
+ });
711
+
712
+ it("should not process any action if adl.nav.request is '_none_' and autoProgress is false", async () => {
713
+ terminateStub.resolves(APIConstants.global.SCORM_TRUE);
714
+ scorm2004API.adl.nav.request = "_none_";
715
+ scorm2004API.settings.autoProgress = false;
716
+ await scorm2004API.internalFinish();
717
+ expect(processListenersSpy.called).toBe(false);
718
+ });
719
+
720
+ it("should return the result of terminate", async () => {
721
+ terminateStub.resolves(APIConstants.global.SCORM_TRUE);
722
+ const result = await scorm2004API.internalFinish();
723
+ expect(result).toEqual(APIConstants.global.SCORM_TRUE);
724
+ });
725
+ });
726
+ });
727
+
728
+ describe("lmsCommit()", () => {
729
+ const scorm2004API = api();
730
+
731
+ it("should call commit and return SCORM_TRUE", async () => {
732
+ const commitStub = sinon
733
+ .stub(scorm2004API, "commit")
734
+ .resolves(APIConstants.global.SCORM_TRUE);
735
+ const result = scorm2004API.lmsCommit();
736
+ expect(result).toEqual(APIConstants.global.SCORM_TRUE);
737
+ expect(commitStub.calledOnce).toBe(true);
738
+ commitStub.restore();
739
+ });
740
+ });
741
+
584
742
  describe("loadFromFlattenedJSON()", () => {
585
743
  it("should load data, even if out of order", () => {
586
744
  const scorm2004API = api();
@@ -599,6 +757,8 @@ describe("SCORM 2004 API Tests", () => {
599
757
  "cmi.interactions.0.latency": "PT2M30S",
600
758
  "cmi.interactions.0.objectives.0.id": "Question14_1",
601
759
  "cmi.interactions.0.objectives.0.correct_responses.0.pattern": "CPR",
760
+ "adl.nav.request": "continue",
761
+ "adl.nav.request_valid.choice.{target=sco-id}": "true",
602
762
  },
603
763
  "",
604
764
  );
@@ -617,6 +777,281 @@ describe("SCORM 2004 API Tests", () => {
617
777
  const cmiExport: RefObject = scorm2004API.renderCommitCMI(true);
618
778
  expect(cmiExport.cmi.total_time).toEqual("P1DT12H34M55S");
619
779
  });
780
+
781
+ it("should return flattened format when dataCommitFormat is 'flattened'", function () {
782
+ const scorm2004API = api({
783
+ ...DefaultSettings,
784
+ dataCommitFormat: "flattened",
785
+ });
786
+ const result = scorm2004API.renderCommitCMI(false);
787
+ expect(result).toBeInstanceOf(Object);
788
+ expect({}.hasOwnProperty.call(result, "cmi.learner_id")).toBe(true);
789
+ });
790
+
791
+ it("should return params format when dataCommitFormat is 'params'", function () {
792
+ const scorm2004API = api();
793
+ scorm2004API.settings.dataCommitFormat = "params";
794
+ const result = scorm2004API.renderCommitCMI(false);
795
+ expect(result).toBeInstanceOf(Array);
796
+ expect(result).toContain("cmi.credit=credit");
797
+ // Add more specific assertions based on expected params output
798
+ });
799
+
800
+ it("should return JSON format when dataCommitFormat is 'json'", function () {
801
+ const scorm2004API = api();
802
+ scorm2004API.settings.dataCommitFormat = "json";
803
+ const result = scorm2004API.renderCommitCMI(false) as any;
804
+ expect(result).toBeInstanceOf(Object);
805
+ expect(result).toHaveProperty("cmi");
806
+ expect(result.cmi).toBeInstanceOf(Object);
807
+ expect(result.cmi.credit).toEqual("credit");
808
+ // Add more specific assertions based on expected JSON output
809
+ });
810
+
811
+ it("should include total_time if terminateCommit is true", function () {
812
+ const scorm2004API = api();
813
+ const spy = sinon.spy(scorm2004API.cmi, "getCurrentTotalTime");
814
+ const cmiExport = scorm2004API.renderCommitCMI(true) as any;
815
+ expect(spy.calledOnce).toBe(true);
816
+ expect(cmiExport.cmi.total_time).toEqual(spy.returnValues[0]);
817
+ spy.restore();
818
+ });
819
+
820
+ it("should not include total_time if terminateCommit is false", function () {
821
+ const scorm2004API = api();
822
+ const spy = sinon.spy(scorm2004API.cmi, "getCurrentTotalTime");
823
+ const cmiExport = scorm2004API.renderCommitCMI(false) as any;
824
+ expect(spy.called).toBe(false);
825
+ expect(cmiExport.cmi).not.toHaveProperty("total_time");
826
+ spy.restore();
827
+ });
828
+ });
829
+
830
+ describe("lmsGetDiagnostic()", () => {
831
+ it("should return diagnostic information for a given error code", () => {
832
+ const scorm2004API = api();
833
+ const errorCode = scorm2004_error_codes.GENERAL;
834
+ const diagnosticInfo = scorm2004API.lmsGetDiagnostic(errorCode);
835
+ expect(diagnosticInfo).toEqual(
836
+ APIConstants.scorm2004.error_descriptions[errorCode].detailMessage,
837
+ );
838
+ });
839
+
840
+ it("should return an empty string for an unknown error code", () => {
841
+ const scorm2004API = api();
842
+ const unknownErrorCode = 9999;
843
+ const diagnosticInfo = scorm2004API.lmsGetDiagnostic(unknownErrorCode);
844
+ expect(diagnosticInfo).toEqual("");
845
+ });
846
+ });
847
+
848
+ describe("lmsGetErrorString()", () => {
849
+ it("should return the error string for a given error code", () => {
850
+ const scorm2004API = api();
851
+ const errorCode = scorm2004_error_codes.GENERAL;
852
+ const errorString = scorm2004API.lmsGetErrorString(errorCode);
853
+ expect(errorString).toEqual("General Exception");
854
+ });
855
+
856
+ it("should return an empty string for an unknown error code", () => {
857
+ const scorm2004API = api();
858
+ const unknownErrorCode = 9999;
859
+ const errorString = scorm2004API.lmsGetErrorString(unknownErrorCode);
860
+ expect(errorString).toEqual("");
861
+ });
862
+ });
863
+
864
+ describe("replaceWithAnotherScormAPI()", () => {
865
+ it("should replace the current API with another API", () => {
866
+ const firstAPI = api();
867
+ const secondAPI = api();
868
+
869
+ firstAPI.cmi.learner_id = "student_1";
870
+ firstAPI.adl.nav.request = "continue";
871
+ secondAPI.cmi.learner_id = "student_2";
872
+ secondAPI.adl.nav.request = "exit";
873
+
874
+ firstAPI.replaceWithAnotherScormAPI(secondAPI);
875
+ expect(firstAPI.cmi.learner_id).toEqual("student_2");
876
+ expect(firstAPI.adl.nav.request).toEqual("exit");
877
+ });
878
+ });
879
+
880
+ describe("checkCorrectResponseValue()", () => {
881
+ it("should properly handle the true-false response type for unknown value", () => {
882
+ const scorm2004API = new Scorm2004API();
883
+ scorm2004API.checkCorrectResponseValue("true-false", ["unknown"], "true");
884
+ expect(scorm2004API.lmsGetLastError()).toEqual(
885
+ String(scorm2004_error_codes.TYPE_MISMATCH),
886
+ );
887
+ });
888
+
889
+ it("should properly handle the true-false response type for correct value", () => {
890
+ const scorm2004API = new Scorm2004API();
891
+ scorm2004API.checkCorrectResponseValue("true-false", ["true"], "true");
892
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
893
+ });
894
+
895
+ it("should properly handle the choice response type for value over 4000 characters", () => {
896
+ const scorm2004API = new Scorm2004API();
897
+ scorm2004API.checkCorrectResponseValue(
898
+ "choice",
899
+ ["x".repeat(4001)],
900
+ "true",
901
+ );
902
+ expect(scorm2004API.lmsGetLastError()).toEqual(
903
+ String(scorm2004_error_codes.TYPE_MISMATCH),
904
+ );
905
+ });
906
+
907
+ it("should properly handle the choice response type for correct value", () => {
908
+ const scorm2004API = new Scorm2004API();
909
+ scorm2004API.checkCorrectResponseValue("choice", ["true"], "true");
910
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
911
+ });
912
+
913
+ it("should properly handle the fill-in response type for correct value", () => {
914
+ const scorm2004API = new Scorm2004API();
915
+ scorm2004API.checkCorrectResponseValue("fill-in", ["true"], "true");
916
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
917
+ });
918
+
919
+ it("should properly handle the long-fill-in response type for correct value", () => {
920
+ const scorm2004API = new Scorm2004API();
921
+ scorm2004API.checkCorrectResponseValue("long-fill-in", ["true"], "true");
922
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
923
+ });
924
+
925
+ it("should properly handle the matching response type for correct value", () => {
926
+ const scorm2004API = new Scorm2004API();
927
+ scorm2004API.checkCorrectResponseValue(
928
+ "matching",
929
+ ["{order_matters=true}0[.]1"],
930
+ "true",
931
+ );
932
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
933
+ });
934
+ });
935
+
936
+ describe("removeCorrectResponsePrefixes()", () => {
937
+ it("should remove the prefix from the string", () => {
938
+ const scorm2004API = new Scorm2004API();
939
+ const input = "{order_matters=true}correctResponse";
940
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
941
+ expect(result).toBe("correctResponse");
942
+ });
943
+
944
+ it("should return the original string if no prefix is present", () => {
945
+ const scorm2004API = new Scorm2004API();
946
+ const input = "correctResponse";
947
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
948
+ expect(result).toBe("correctResponse");
949
+ });
950
+
951
+ it("should handle empty strings correctly", () => {
952
+ const scorm2004API = new Scorm2004API();
953
+ const input = "";
954
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
955
+ expect(result).toBe("");
956
+ });
957
+
958
+ it("should handle multiple prefixes correctly", () => {
959
+ const scorm2004API = new Scorm2004API();
960
+ const input =
961
+ "{lang=en}{order_matters=true}{case_matters=false}correctResponse";
962
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
963
+ expect(result).toBe("correctResponse");
964
+ });
965
+
966
+ it("should throw an error for invalid order_matters value", () => {
967
+ const scorm2004API = new Scorm2004API();
968
+ const input = "{order_matters=invalid}correctResponse";
969
+ scorm2004API.removeCorrectResponsePrefixes(input);
970
+ expect(scorm2004API.lmsGetLastError()).toEqual(
971
+ String(scorm2004_error_codes.TYPE_MISMATCH),
972
+ );
973
+ });
974
+
975
+ it("should throw an error for invalid case_matters value", () => {
976
+ const scorm2004API = new Scorm2004API();
977
+ const input = "{case_matters=invalid}correctResponse";
978
+ scorm2004API.removeCorrectResponsePrefixes(input);
979
+ expect(scorm2004API.lmsGetLastError()).toEqual(
980
+ String(scorm2004_error_codes.TYPE_MISMATCH),
981
+ );
982
+ });
983
+
984
+ it("should ignore an unknown prefix", () => {
985
+ const scorm2004API = new Scorm2004API();
986
+ const input = "{unknown=true}correctResponse";
987
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
988
+ expect(result).toBe("{unknown=true}correctResponse");
989
+ });
990
+
991
+ it("should throw an error with an invalid language code", () => {
992
+ const scorm2004API = new Scorm2004API();
993
+ const input = "{lang=xyz}correctResponse";
994
+ scorm2004API.removeCorrectResponsePrefixes(input);
995
+ expect(scorm2004API.lmsGetLastError()).toEqual(
996
+ String(scorm2004_error_codes.TYPE_MISMATCH),
997
+ );
998
+ });
999
+ });
1000
+
1001
+ describe("getChildElement()", () => {
1002
+ it("should handle missing interaction type", () => {
1003
+ const scorm2004API = apiInitialized({
1004
+ cmi: {
1005
+ interactions: {
1006
+ childArray: [{ id: "interaction-id-1" }],
1007
+ },
1008
+ },
1009
+ });
1010
+ const result = scorm2004API.getChildElement(
1011
+ "cmi.interactions.0.correct_responses.0",
1012
+ "response-value",
1013
+ true,
1014
+ );
1015
+ expect(result).toBeNull();
1016
+ expect(scorm2004API.lmsGetLastError()).toEqual(
1017
+ String(scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED),
1018
+ );
1019
+ });
1020
+
1021
+ it("should call throwSCORMError with the correct arguments in createCorrectResponsesObject", () => {
1022
+ const scorm2004API = new Scorm2004API();
1023
+ const interaction = {
1024
+ id: "interaction-id-1",
1025
+ type: "invalid-type",
1026
+ correct_responses: { _count: 1 },
1027
+ };
1028
+
1029
+ // Initialize the childArray with an empty array
1030
+ scorm2004API.cmi.interactions.childArray = [];
1031
+
1032
+ // Add the interaction object to the childArray
1033
+ scorm2004API.cmi.interactions.childArray[0] = interaction;
1034
+
1035
+ sinon.stub(scorm2004API, "isInitialized").returns(true);
1036
+ const throwSCORMErrorSpy = sinon.spy(scorm2004API, "throwSCORMError");
1037
+
1038
+ try {
1039
+ scorm2004API.getChildElement(
1040
+ "cmi.interactions.0.correct_responses.0",
1041
+ "response-value",
1042
+ true,
1043
+ );
1044
+ } catch (e) {
1045
+ // Expected to throw, so we catch it to prevent the test from failing
1046
+ }
1047
+
1048
+ expect(
1049
+ throwSCORMErrorSpy.calledWith(
1050
+ ErrorCodes.scorm2004.GENERAL_SET_FAILURE,
1051
+ "Incorrect Response Type: " + interaction.type,
1052
+ ),
1053
+ ).toBe(true);
1054
+ });
620
1055
  });
621
1056
 
622
1057
  describe("storeData()", () => {
@@ -809,4 +1244,55 @@ describe("SCORM 2004 API Tests", () => {
809
1244
  expect(callback.called).toBe(true);
810
1245
  });
811
1246
  });
1247
+
1248
+ describe("Test issues from users", () => {
1249
+ it("Should be able to load the JSON data from issue #678", () => {
1250
+ const scorm20004api = api(
1251
+ {
1252
+ ...DefaultSettings,
1253
+ autocommit: false,
1254
+ lmsCommitUrl: "/scorm2004",
1255
+ logLevel: 1,
1256
+ },
1257
+ {
1258
+ comments_from_learner: {},
1259
+ comments_from_lms: {},
1260
+ completion_status: "incomplete",
1261
+ completion_threshold: "",
1262
+ credit: "credit",
1263
+ entry: "resume",
1264
+ exit: "suspend",
1265
+ interactions: {},
1266
+ launch_data: "",
1267
+ learner_id: "Test",
1268
+ learner_name: "User,Sample",
1269
+ learner_preference: {
1270
+ audio_level: "1",
1271
+ language: "",
1272
+ delivery_speed: "1",
1273
+ audio_captioning: "0",
1274
+ },
1275
+ location: "1",
1276
+ max_time_allowed: "",
1277
+ mode: "normal",
1278
+ objectives: {},
1279
+ progress_measure: "",
1280
+ scaled_passing_score: "",
1281
+ score: {
1282
+ scaled: "",
1283
+ raw: "",
1284
+ min: "",
1285
+ max: "",
1286
+ },
1287
+ session_time: "PT19.57S",
1288
+ success_status: "unknown",
1289
+ suspend_data: "",
1290
+ time_limit_action: "continue,no message",
1291
+ total_time: "PT19.57S",
1292
+ },
1293
+ );
1294
+
1295
+ expect(scorm20004api.cmi.learner_preference).not.toBeNull();
1296
+ });
1297
+ });
812
1298
  });