scorm-again 2.0.0 → 2.2.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 +180 -72
  5. package/dist/aicc.js +1520 -1149
  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 +2812 -2205
  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 +1129 -842
  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 +1921 -1564
  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 +20 -17
  22. package/src/AICC.ts +15 -17
  23. package/src/BaseAPI.ts +283 -420
  24. package/src/Scorm12API.ts +133 -41
  25. package/src/Scorm2004API.ts +224 -120
  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 +82 -0
  56. package/src/constants/enums.ts +17 -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 +50 -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 +372 -9
  67. package/test/Scorm2004API.spec.ts +558 -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,351 @@ 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("renderCommitObject()", () => {
831
+ it("should render commit object with default settings and no score", () => {
832
+ const scorm2004API = api();
833
+ scorm2004API.cmi.completion_status = "incomplete";
834
+ scorm2004API.cmi.total_time = "P12H34M56S";
835
+ scorm2004API.cmi.session_time = "P23H59M59S";
836
+ const commitObject = scorm2004API.renderCommitObject(true);
837
+ expect(commitObject.successStatus).toEqual("unknown");
838
+ expect(commitObject.completionStatus).toEqual("incomplete");
839
+ expect(commitObject.runtimeData.cmi.completion_status).toEqual(
840
+ "incomplete",
841
+ );
842
+ expect(commitObject.totalTimeSeconds).toEqual(
843
+ 12 * 3600 + 34 * 60 + 56 + (23 * 3600 + 59 * 60 + 59),
844
+ );
845
+ });
846
+
847
+ it("should render commit object with score data", () => {
848
+ const scorm2004API = api();
849
+ scorm2004API.cmi.completion_status = "completed";
850
+ scorm2004API.cmi.score.raw = "85";
851
+ scorm2004API.cmi.score.min = "0";
852
+ scorm2004API.cmi.score.max = "100";
853
+ scorm2004API.cmi.score.scaled = "0.85";
854
+ const commitObject = scorm2004API.renderCommitObject(true);
855
+ expect(commitObject.successStatus).toEqual("unknown");
856
+ expect(commitObject.completionStatus).toEqual("completed");
857
+ expect(commitObject.runtimeData.cmi.completion_status).toEqual(
858
+ "completed",
859
+ );
860
+ expect(commitObject.runtimeData.cmi.score.raw).toEqual("85");
861
+ expect(commitObject.runtimeData.cmi.score.min).toEqual("0");
862
+ expect(commitObject.runtimeData.cmi.score.max).toEqual("100");
863
+ expect(commitObject.runtimeData.cmi.score.scaled).toEqual("0.85");
864
+ expect(commitObject.totalTimeSeconds).toEqual(0);
865
+ expect(commitObject.score).toEqual({
866
+ raw: 85,
867
+ min: 0,
868
+ max: 100,
869
+ scaled: 0.85,
870
+ });
871
+ });
872
+
873
+ it("should render commit object with completion and success status", () => {
874
+ const scorm2004API = api();
875
+ scorm2004API.cmi.success_status = "passed";
876
+ const commitObject = scorm2004API.renderCommitObject(true);
877
+ expect(commitObject.successStatus).toEqual("passed");
878
+ expect(commitObject.completionStatus).toEqual("unknown");
879
+ expect(commitObject.runtimeData.cmi.success_status).toEqual("passed");
880
+ });
881
+
882
+ it("should render commit object with failed success status", () => {
883
+ const scorm2004API = api();
884
+ scorm2004API.cmi.success_status = "failed";
885
+ const commitObject = scorm2004API.renderCommitObject(true);
886
+ expect(commitObject.successStatus).toEqual("failed");
887
+ expect(commitObject.completionStatus).toEqual("unknown");
888
+ expect(commitObject.runtimeData.cmi.success_status).toEqual("failed");
889
+ });
890
+
891
+ it("should calculate total time when terminateCommit is true", () => {
892
+ const scorm2004API = api();
893
+ scorm2004API.cmi.total_time = "P12H34M56S";
894
+ scorm2004API.cmi.session_time = "P23H59M59S";
895
+ const commitObject = scorm2004API.renderCommitObject(true);
896
+ expect(commitObject.runtimeData.cmi.total_time).toEqual("P1DT12H34M55S");
897
+ });
898
+ });
899
+
900
+ describe("lmsGetDiagnostic()", () => {
901
+ it("should return diagnostic information for a given error code", () => {
902
+ const scorm2004API = api();
903
+ const errorCode = scorm2004_error_codes.GENERAL;
904
+ const diagnosticInfo = scorm2004API.lmsGetDiagnostic(errorCode);
905
+ expect(diagnosticInfo).toEqual(
906
+ APIConstants.scorm2004.error_descriptions[errorCode].detailMessage,
907
+ );
908
+ });
909
+
910
+ it("should return an empty string for an unknown error code", () => {
911
+ const scorm2004API = api();
912
+ const unknownErrorCode = 9999;
913
+ const diagnosticInfo = scorm2004API.lmsGetDiagnostic(unknownErrorCode);
914
+ expect(diagnosticInfo).toEqual("");
915
+ });
916
+ });
917
+
918
+ describe("lmsGetErrorString()", () => {
919
+ it("should return the error string for a given error code", () => {
920
+ const scorm2004API = api();
921
+ const errorCode = scorm2004_error_codes.GENERAL;
922
+ const errorString = scorm2004API.lmsGetErrorString(errorCode);
923
+ expect(errorString).toEqual("General Exception");
924
+ });
925
+
926
+ it("should return an empty string for an unknown error code", () => {
927
+ const scorm2004API = api();
928
+ const unknownErrorCode = 9999;
929
+ const errorString = scorm2004API.lmsGetErrorString(unknownErrorCode);
930
+ expect(errorString).toEqual("");
931
+ });
932
+ });
933
+
934
+ describe("replaceWithAnotherScormAPI()", () => {
935
+ it("should replace the current API with another API", () => {
936
+ const firstAPI = api();
937
+ const secondAPI = api();
938
+
939
+ firstAPI.cmi.learner_id = "student_1";
940
+ firstAPI.adl.nav.request = "continue";
941
+ secondAPI.cmi.learner_id = "student_2";
942
+ secondAPI.adl.nav.request = "exit";
943
+
944
+ firstAPI.replaceWithAnotherScormAPI(secondAPI);
945
+ expect(firstAPI.cmi.learner_id).toEqual("student_2");
946
+ expect(firstAPI.adl.nav.request).toEqual("exit");
947
+ });
948
+ });
949
+
950
+ describe("checkCorrectResponseValue()", () => {
951
+ it("should properly handle the true-false response type for unknown value", () => {
952
+ const scorm2004API = new Scorm2004API();
953
+ scorm2004API.checkCorrectResponseValue("true-false", ["unknown"], "true");
954
+ expect(scorm2004API.lmsGetLastError()).toEqual(
955
+ String(scorm2004_error_codes.TYPE_MISMATCH),
956
+ );
957
+ });
958
+
959
+ it("should properly handle the true-false response type for correct value", () => {
960
+ const scorm2004API = new Scorm2004API();
961
+ scorm2004API.checkCorrectResponseValue("true-false", ["true"], "true");
962
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
963
+ });
964
+
965
+ it("should properly handle the choice response type for value over 4000 characters", () => {
966
+ const scorm2004API = new Scorm2004API();
967
+ scorm2004API.checkCorrectResponseValue(
968
+ "choice",
969
+ ["x".repeat(4001)],
970
+ "true",
971
+ );
972
+ expect(scorm2004API.lmsGetLastError()).toEqual(
973
+ String(scorm2004_error_codes.TYPE_MISMATCH),
974
+ );
975
+ });
976
+
977
+ it("should properly handle the choice response type for correct value", () => {
978
+ const scorm2004API = new Scorm2004API();
979
+ scorm2004API.checkCorrectResponseValue("choice", ["true"], "true");
980
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
981
+ });
982
+
983
+ it("should properly handle the fill-in response type for correct value", () => {
984
+ const scorm2004API = new Scorm2004API();
985
+ scorm2004API.checkCorrectResponseValue("fill-in", ["true"], "true");
986
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
987
+ });
988
+
989
+ it("should properly handle the long-fill-in response type for correct value", () => {
990
+ const scorm2004API = new Scorm2004API();
991
+ scorm2004API.checkCorrectResponseValue("long-fill-in", ["true"], "true");
992
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
993
+ });
994
+
995
+ it("should properly handle the matching response type for correct value", () => {
996
+ const scorm2004API = new Scorm2004API();
997
+ scorm2004API.checkCorrectResponseValue(
998
+ "matching",
999
+ ["{order_matters=true}0[.]1"],
1000
+ "true",
1001
+ );
1002
+ expect(scorm2004API.lmsGetLastError()).toEqual(String(0));
1003
+ });
1004
+ });
1005
+
1006
+ describe("removeCorrectResponsePrefixes()", () => {
1007
+ it("should remove the prefix from the string", () => {
1008
+ const scorm2004API = new Scorm2004API();
1009
+ const input = "{order_matters=true}correctResponse";
1010
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
1011
+ expect(result).toBe("correctResponse");
1012
+ });
1013
+
1014
+ it("should return the original string if no prefix is present", () => {
1015
+ const scorm2004API = new Scorm2004API();
1016
+ const input = "correctResponse";
1017
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
1018
+ expect(result).toBe("correctResponse");
1019
+ });
1020
+
1021
+ it("should handle empty strings correctly", () => {
1022
+ const scorm2004API = new Scorm2004API();
1023
+ const input = "";
1024
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
1025
+ expect(result).toBe("");
1026
+ });
1027
+
1028
+ it("should handle multiple prefixes correctly", () => {
1029
+ const scorm2004API = new Scorm2004API();
1030
+ const input =
1031
+ "{lang=en}{order_matters=true}{case_matters=false}correctResponse";
1032
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
1033
+ expect(result).toBe("correctResponse");
1034
+ });
1035
+
1036
+ it("should throw an error for invalid order_matters value", () => {
1037
+ const scorm2004API = new Scorm2004API();
1038
+ const input = "{order_matters=invalid}correctResponse";
1039
+ scorm2004API.removeCorrectResponsePrefixes(input);
1040
+ expect(scorm2004API.lmsGetLastError()).toEqual(
1041
+ String(scorm2004_error_codes.TYPE_MISMATCH),
1042
+ );
1043
+ });
1044
+
1045
+ it("should throw an error for invalid case_matters value", () => {
1046
+ const scorm2004API = new Scorm2004API();
1047
+ const input = "{case_matters=invalid}correctResponse";
1048
+ scorm2004API.removeCorrectResponsePrefixes(input);
1049
+ expect(scorm2004API.lmsGetLastError()).toEqual(
1050
+ String(scorm2004_error_codes.TYPE_MISMATCH),
1051
+ );
1052
+ });
1053
+
1054
+ it("should ignore an unknown prefix", () => {
1055
+ const scorm2004API = new Scorm2004API();
1056
+ const input = "{unknown=true}correctResponse";
1057
+ const result = scorm2004API.removeCorrectResponsePrefixes(input);
1058
+ expect(result).toBe("{unknown=true}correctResponse");
1059
+ });
1060
+
1061
+ it("should throw an error with an invalid language code", () => {
1062
+ const scorm2004API = new Scorm2004API();
1063
+ const input = "{lang=xyz}correctResponse";
1064
+ scorm2004API.removeCorrectResponsePrefixes(input);
1065
+ expect(scorm2004API.lmsGetLastError()).toEqual(
1066
+ String(scorm2004_error_codes.TYPE_MISMATCH),
1067
+ );
1068
+ });
1069
+ });
1070
+
1071
+ describe("getChildElement()", () => {
1072
+ it("should handle missing interaction type", () => {
1073
+ const scorm2004API = apiInitialized({
1074
+ cmi: {
1075
+ interactions: {
1076
+ childArray: [{ id: "interaction-id-1" }],
1077
+ },
1078
+ },
1079
+ });
1080
+ const result = scorm2004API.getChildElement(
1081
+ "cmi.interactions.0.correct_responses.0",
1082
+ "response-value",
1083
+ true,
1084
+ );
1085
+ expect(result).toBeNull();
1086
+ expect(scorm2004API.lmsGetLastError()).toEqual(
1087
+ String(scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED),
1088
+ );
1089
+ });
1090
+
1091
+ it("should call throwSCORMError with the correct arguments in createCorrectResponsesObject", () => {
1092
+ const scorm2004API = new Scorm2004API();
1093
+ const interaction = {
1094
+ id: "interaction-id-1",
1095
+ type: "invalid-type",
1096
+ correct_responses: { _count: 1 },
1097
+ };
1098
+
1099
+ // Initialize the childArray with an empty array
1100
+ scorm2004API.cmi.interactions.childArray = [];
1101
+
1102
+ // Add the interaction object to the childArray
1103
+ scorm2004API.cmi.interactions.childArray[0] = interaction;
1104
+
1105
+ sinon.stub(scorm2004API, "isInitialized").returns(true);
1106
+ const throwSCORMErrorSpy = sinon.spy(scorm2004API, "throwSCORMError");
1107
+
1108
+ try {
1109
+ scorm2004API.getChildElement(
1110
+ "cmi.interactions.0.correct_responses.0",
1111
+ "response-value",
1112
+ true,
1113
+ );
1114
+ } catch (e) {
1115
+ // Expected to throw, so we catch it to prevent the test from failing
1116
+ }
1117
+
1118
+ expect(
1119
+ throwSCORMErrorSpy.calledWith(
1120
+ ErrorCodes.scorm2004.GENERAL_SET_FAILURE,
1121
+ "Incorrect Response Type: " + interaction.type,
1122
+ ),
1123
+ ).toBe(true);
1124
+ });
620
1125
  });
621
1126
 
622
1127
  describe("storeData()", () => {
@@ -809,4 +1314,55 @@ describe("SCORM 2004 API Tests", () => {
809
1314
  expect(callback.called).toBe(true);
810
1315
  });
811
1316
  });
1317
+
1318
+ describe("Test issues from users", () => {
1319
+ it("Should be able to load the JSON data from issue #678", () => {
1320
+ const scorm20004api = api(
1321
+ {
1322
+ ...DefaultSettings,
1323
+ autocommit: false,
1324
+ lmsCommitUrl: "/scorm2004",
1325
+ logLevel: 1,
1326
+ },
1327
+ {
1328
+ comments_from_learner: {},
1329
+ comments_from_lms: {},
1330
+ completion_status: "incomplete",
1331
+ completion_threshold: "",
1332
+ credit: "credit",
1333
+ entry: "resume",
1334
+ exit: "suspend",
1335
+ interactions: {},
1336
+ launch_data: "",
1337
+ learner_id: "Test",
1338
+ learner_name: "User,Sample",
1339
+ learner_preference: {
1340
+ audio_level: "1",
1341
+ language: "",
1342
+ delivery_speed: "1",
1343
+ audio_captioning: "0",
1344
+ },
1345
+ location: "1",
1346
+ max_time_allowed: "",
1347
+ mode: "normal",
1348
+ objectives: {},
1349
+ progress_measure: "",
1350
+ scaled_passing_score: "",
1351
+ score: {
1352
+ scaled: "",
1353
+ raw: "",
1354
+ min: "",
1355
+ max: "",
1356
+ },
1357
+ session_time: "PT19.57S",
1358
+ success_status: "unknown",
1359
+ suspend_data: "",
1360
+ time_limit_action: "continue,no message",
1361
+ total_time: "PT19.57S",
1362
+ },
1363
+ );
1364
+
1365
+ expect(scorm20004api.cmi.learner_preference).not.toBeNull();
1366
+ });
1367
+ });
812
1368
  });