scorm_engine 0.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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/Rakefile +13 -0
  4. data/lib/scorm_engine.rb +13 -0
  5. data/lib/scorm_engine/api/endpoints.rb +50 -0
  6. data/lib/scorm_engine/api/endpoints/about.rb +59 -0
  7. data/lib/scorm_engine/api/endpoints/courses.rb +136 -0
  8. data/lib/scorm_engine/api/endpoints/courses/configuration.rb +146 -0
  9. data/lib/scorm_engine/api/endpoints/courses/import.rb +83 -0
  10. data/lib/scorm_engine/api/endpoints/ping.rb +20 -0
  11. data/lib/scorm_engine/api/endpoints/registrations.rb +321 -0
  12. data/lib/scorm_engine/api/endpoints/registrations/configuration.rb +146 -0
  13. data/lib/scorm_engine/api/endpoints/registrations/launch_history.rb +47 -0
  14. data/lib/scorm_engine/client.rb +17 -0
  15. data/lib/scorm_engine/configuration.rb +43 -0
  16. data/lib/scorm_engine/faraday/connection.rb +35 -0
  17. data/lib/scorm_engine/faraday/request.rb +54 -0
  18. data/lib/scorm_engine/models.rb +9 -0
  19. data/lib/scorm_engine/models/course.rb +66 -0
  20. data/lib/scorm_engine/models/course_configuration.rb +37 -0
  21. data/lib/scorm_engine/models/course_import.rb +42 -0
  22. data/lib/scorm_engine/models/learner.rb +24 -0
  23. data/lib/scorm_engine/models/registration.rb +82 -0
  24. data/lib/scorm_engine/models/registration_activity_detail.rb +37 -0
  25. data/lib/scorm_engine/models/registration_configuration.rb +37 -0
  26. data/lib/scorm_engine/models/registration_launch_history.rb +92 -0
  27. data/lib/scorm_engine/models/registration_runtime_interaction.rb +52 -0
  28. data/lib/scorm_engine/response.rb +26 -0
  29. data/lib/scorm_engine/utils.rb +13 -0
  30. data/lib/scorm_engine/version.rb +3 -0
  31. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_About/_get_about/is_successful.yml +38 -0
  32. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_About/_get_about/knows_the_platform.yml +38 -0
  33. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_About/_get_about/knows_the_version.yml +38 -0
  34. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_About/_get_about_user_count/accepts_before_option.yml +38 -0
  35. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_About/_get_about_user_count/accepts_since_option.yml +38 -0
  36. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_About/_get_about_user_count/is_successful.yml +38 -0
  37. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_About/_get_about_user_count/tracks_combined_counts.yml +38 -0
  38. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_About/_get_about_user_count/tracks_per_tenantcounts.yml +38 -0
  39. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_delete_course/fails_when_id_is_invalid.yml +38 -0
  40. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_delete_course/works.yml +32 -0
  41. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_course_detail/is_successful.yml +41 -0
  42. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_course_detail/results/sucessfully_creates_the_Course_attributes.yml +41 -0
  43. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_course_preview/fails_when_id_is_invalid.yml +38 -0
  44. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_course_preview/is_successful.yml +38 -0
  45. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_course_preview/results/returns_a_URL_string.yml +38 -0
  46. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/_course_id_option/fetches_a_single_course_but_perhaps_multiple_versions.yml +39 -0
  47. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/_course_id_option/returns_404_when_ID_is_invalid.yml +38 -0
  48. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/_more_option_pagination_/returns_all_the_courses.yml +89 -0
  49. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/_more_option_pagination_/returns_the_more_key_in_the_raw_response.yml +48 -0
  50. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/_since_option/fails_when_passed_an_invalid_value.yml +37 -0
  51. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/_since_option/works.yml +43 -0
  52. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/is_successful.yml +43 -0
  53. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/results/is_an_enumerator_of_Course_models.yml +43 -0
  54. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses/_get_courses/results/sucessfully_creates_the_Course_attributes.yml +43 -0
  55. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_get_course_configuration/fails_when_id_is_invalid.yml +38 -0
  56. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_get_course_configuration/is_successful.yml +38 -0
  57. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_get_course_configuration/results/makes_settings_available_as_key/value_pairs.yml +38 -0
  58. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_get_course_configuration_setting/fails_when_course_id_is_invalid.yml +38 -0
  59. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_get_course_configuration_setting/fails_when_setting_id_is_invalid.yml +36 -0
  60. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_get_course_configuration_setting/is_successful.yml +65 -0
  61. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_get_course_configuration_setting/results/returns_the_value_as_a_string.yml +65 -0
  62. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_post_course_configuration/fails_when_id_is_invalid.yml +38 -0
  63. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_post_course_configuration/fails_when_settings_are_invalid.yml +36 -0
  64. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_post_course_configuration/is_successful.yml +32 -0
  65. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_post_course_configuration/persists_the_settings.yml +131 -0
  66. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_put_course_configuration_setting/fails_when_course_id_is_invalid.yml +38 -0
  67. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_put_course_configuration_setting/fails_when_setting_id_is_invalid.yml +36 -0
  68. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_put_course_configuration_setting/is_successful.yml +65 -0
  69. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Configuration/_put_course_configuration_setting/results/persists_the_changes.yml +98 -0
  70. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Import/_get_course_import/successful_imports/works.yml +147 -0
  71. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Import/_get_course_import/unsuccessful_imports/fails_to_import_given_an_invalid_url.yml +326 -0
  72. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Import/_post_course_import/successful_imports/works.yml +38 -0
  73. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Courses_Import/_post_course_import/unsuccessful_imports/fails_to_import_a_previously_existing_course.yml +36 -0
  74. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Ping/_get_ping/is_successful.yml +38 -0
  75. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Ping/_get_ping/reports_the_api_is_up.yml +38 -0
  76. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Ping/_get_ping/with_invalid_password/is_unsuccessful.yml +41 -0
  77. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Ping/_get_ping/with_invalid_password/returns_status_403.yml +41 -0
  78. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_delete_registration/is_failure_when_registration_does_not_exist.yml +38 -0
  79. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_delete_registration/is_successful_when_registration_exists.yml +32 -0
  80. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_exists/is_false_when_registration_does_not_exist.yml +38 -0
  81. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_exists/is_true_when_registration_exists.yml +36 -0
  82. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_instances/includes_results_we_expect.yml +39 -0
  83. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_instances/is_successful.yml +39 -0
  84. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_instances/returns_an_array_of_registrations.yml +39 -0
  85. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_launch_link/fails_when_id_is_invalid.yml +38 -0
  86. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_launch_link/is_successful.yml +38 -0
  87. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_launch_link/results/returns_a_URL_string.yml +38 -0
  88. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_progress/detail/does_not_return_activity_details_by_default.yml +39 -0
  89. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_progress/detail/returns_activity_details_if_requested.yml +41 -0
  90. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_progress/fails_when_registration_does_not_exist.yml +38 -0
  91. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registration_progress/returns_a_registration_when_it_exists.yml +39 -0
  92. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registrations/filtering_by_course_id/excludes_results.yml +38 -0
  93. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registrations/filtering_by_course_id/includes_results.yml +40 -0
  94. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registrations/filtering_by_learner_id/excludes_results.yml +38 -0
  95. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registrations/filtering_by_learner_id/includes_results.yml +39 -0
  96. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registrations/includes_results_we_expect.yml +40 -0
  97. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registrations/is_successful.yml +40 -0
  98. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_get_registrations/returns_an_array_of_registrations.yml +40 -0
  99. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_post_registration/fails_if_course_id_is_invalid.yml +36 -0
  100. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_post_registration/fails_if_registration_id_already_exists.yml +36 -0
  101. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations/_post_registration/is_successful.yml +61 -0
  102. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_get_registration_configuration/fails_when_id_is_invalid.yml +38 -0
  103. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_get_registration_configuration/is_successful.yml +38 -0
  104. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_get_registration_configuration/results/makes_settings_available_as_key/value_pairs.yml +38 -0
  105. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_get_registration_configuration_setting/fails_when_registration_id_is_invalid.yml +38 -0
  106. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_get_registration_configuration_setting/fails_when_setting_id_is_invalid.yml +36 -0
  107. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_get_registration_configuration_setting/is_successful.yml +65 -0
  108. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_get_registration_configuration_setting/results/returns_the_value_as_a_string.yml +65 -0
  109. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_post_registration_configuration/fails_when_id_is_invalid.yml +38 -0
  110. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_post_registration_configuration/fails_when_settings_are_invalid.yml +36 -0
  111. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_post_registration_configuration/is_successful.yml +32 -0
  112. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_post_registration_configuration/persists_the_settings.yml +131 -0
  113. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_put_registration_configuration_setting/fails_when_registration_id_is_invalid.yml +38 -0
  114. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_put_registration_configuration_setting/fails_when_setting_id_is_invalid.yml +36 -0
  115. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_put_registration_configuration_setting/is_successful.yml +65 -0
  116. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_Configuration/_put_registration_configuration_setting/results/persists_the_changes.yml +98 -0
  117. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_LaunchHistory/_get_registration_launch_history/fails_when_registration_does_not_exist.yml +38 -0
  118. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_LaunchHistory/_get_registration_launch_history/is_successful.yml +54 -0
  119. data/spec/fixtures/vcr/ScormEngine_Api_Endpoints_Registrations_LaunchHistory/_get_registration_launch_history/returns_an_array_of_registration_launch_histories.yml +54 -0
  120. data/spec/fixtures/zip/Non-working SCORM.zip +0 -0
  121. data/spec/fixtures/zip/RuntimeBasicCalls_SCORM20043rdEdition.zip +0 -0
  122. data/spec/scorm_engine/api/endpoints/about_spec.rb +59 -0
  123. data/spec/scorm_engine/api/endpoints/courses/configuration_spec.rb +143 -0
  124. data/spec/scorm_engine/api/endpoints/courses/import_spec.rb +92 -0
  125. data/spec/scorm_engine/api/endpoints/courses_spec.rb +155 -0
  126. data/spec/scorm_engine/api/endpoints/ping_spec.rb +27 -0
  127. data/spec/scorm_engine/api/endpoints/registrations/configuration_spec.rb +154 -0
  128. data/spec/scorm_engine/api/endpoints/registrations/launch_history_spec.rb +46 -0
  129. data/spec/scorm_engine/api/endpoints/registrations_spec.rb +201 -0
  130. data/spec/scorm_engine/configuration_spec.rb +29 -0
  131. data/spec/scorm_engine/models/course_configuration_spec.rb +16 -0
  132. data/spec/scorm_engine/models/course_import_spec.rb +86 -0
  133. data/spec/scorm_engine/models/course_spec.rb +92 -0
  134. data/spec/scorm_engine/models/learner_spec.rb +27 -0
  135. data/spec/scorm_engine/models/registration_activity_detail_spec.rb +101 -0
  136. data/spec/scorm_engine/models/registration_launch_history_spec.rb +86 -0
  137. data/spec/scorm_engine/models/registration_runtime_interaction_spec.rb +91 -0
  138. data/spec/scorm_engine/models/registration_spec.rb +82 -0
  139. data/spec/scorm_engine/utils_spec.rb +15 -0
  140. data/spec/scorm_engine/version_spec.rb +5 -0
  141. data/spec/scorm_engine_spec.rb +2 -0
  142. data/spec/spec_helper.rb +47 -0
  143. data/spec/support/scorm_engine.rb +72 -0
  144. data/spec/support/scorm_engine_client.rb +6 -0
  145. data/spec/support/scorm_engine_configuration.rb +7 -0
  146. data/spec/support/vcr.rb +53 -0
  147. metadata +329 -0
@@ -0,0 +1,101 @@
1
+ RSpec.describe ScormEngine::Models::RegistrationActivityDetail do
2
+ describe ".new_from_api" do
3
+ let(:options) { {
4
+ "id" => "activity-123",
5
+ "runtime" => {
6
+ "runtimeInteractions" => [
7
+ { "id" => "root-interaction-1" },
8
+ { "id" => "root-interaction-2" },
9
+ ],
10
+ },
11
+ "children" => [
12
+ {
13
+ "id" => "child-1",
14
+ "runtime" => {
15
+ "runtimeInteractions" => [
16
+ { "id" => "child-1-interaction-1" },
17
+ { "id" => "child-1-interaction-2" },
18
+ ],
19
+ },
20
+ "children" => [
21
+ {
22
+ "id" => "grandchild-1",
23
+ "runtime" => { "runtimeInteractions" => [] },
24
+ },
25
+ ],
26
+ },
27
+ {
28
+ "id" => "child-2",
29
+ "runtime" => {
30
+ "runtimeInteractions" => [
31
+ { "id" => "child-2-interaction-1" },
32
+ ],
33
+ },
34
+ "children" => [
35
+ {
36
+ "id" => "grandchild-2",
37
+ "runtime" => {
38
+ "runtimeInteractions" => [
39
+ { "id" => "grandchild-1-interaction-1" },
40
+ { "id" => "grandchild-1-interaction-2" },
41
+ ],
42
+ },
43
+ },
44
+ {
45
+ "id" => "grandchild-3",
46
+ "runtime" => {},
47
+ },
48
+ ],
49
+ },
50
+ ],
51
+ } }
52
+
53
+ let(:activity) { described_class.new_from_api(options) }
54
+
55
+ describe ":id" do
56
+ it "is set properly" do
57
+ expect(activity.id).to eq "activity-123"
58
+ end
59
+ end
60
+
61
+ describe ":children" do
62
+ it "is set properly" do
63
+ ids = activity.children.map(&:id)
64
+ expect(ids).to match_array %w[child-1 child-2]
65
+ end
66
+
67
+ it "is set properly for children" do
68
+ ids = activity.children.flat_map(&:children).map(&:id)
69
+ expect(ids).to match_array %w[grandchild-1 grandchild-2 grandchild-3]
70
+ end
71
+ end
72
+
73
+ describe ":runtime_interactions" do
74
+ it "is set properly" do
75
+ ids = activity.runtime_interactions.map(&:id)
76
+ expect(ids).to match_array %w[root-interaction-1 root-interaction-2]
77
+ end
78
+
79
+ it "is set properly for children" do
80
+ ids = activity.children.detect { |c| c.id == "child-1" }.runtime_interactions.map(&:id)
81
+ expect(ids).to match_array %w[child-1-interaction-1 child-1-interaction-2]
82
+ end
83
+
84
+ it "is set properly for grandchildren" do
85
+ child = activity.children.detect { |c| c.id == "child-2" }
86
+ ids = child.children.detect { |c| c.id == "grandchild-2" }.runtime_interactions.map(&:id)
87
+ expect(ids).to match_array %w[grandchild-1-interaction-1 grandchild-1-interaction-2]
88
+ end
89
+ end
90
+
91
+ describe "#all_runtime_interactions" do
92
+ it "returns all interactions" do
93
+ expect(activity.all_runtime_interactions.map(&:id)).to match_array %w[
94
+ root-interaction-1 root-interaction-2
95
+ child-1-interaction-1 child-1-interaction-2 child-2-interaction-1
96
+ grandchild-1-interaction-1 grandchild-1-interaction-2
97
+ ]
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,86 @@
1
+ RSpec.describe ScormEngine::Models::RegistrationLaunchHistory do
2
+ describe ".new_from_api" do
3
+ let(:launch_history) { described_class.new_from_api(
4
+ "id" => "registration-456",
5
+ "instanceId" => 1.0,
6
+ "launchTime" => "07/25/2018 20:02:02 PM",
7
+ "launchTimeUtc" => "07/25/2018 20:02:02 PM",
8
+ "exitTime" => "07/25/2018 20:02:14 PM",
9
+ "exitTimeUtc" => "07/25/2018 20:02:14 PM",
10
+ "completionStatus" => "Incomplete",
11
+ "successStatus" => "Failed",
12
+ "lastRuntimeUpdate" => "07/25/2018 20:02:14 PM",
13
+ "lastRuntimeUpdateUtc" => "2018-07-25T20:01:58.000Z", # intentionally tweaked from what is really returned
14
+ "score" => { "scaled" => 93.0 },
15
+ )}
16
+
17
+ describe ":id" do
18
+ it "is set properly" do
19
+ expect(launch_history.id).to eq "registration-456"
20
+ end
21
+ end
22
+
23
+ describe ":score" do
24
+ it "is set properly" do
25
+ expect(launch_history.score).to eq 93.0
26
+ end
27
+
28
+ it "is left unset if not present" do
29
+ launch_history = described_class.new_from_api({})
30
+ expect(launch_history.score).to eq nil
31
+ end
32
+ end
33
+
34
+ describe ":total_seconds_tracked" do
35
+ it "is set properly when set in the root level with a trailing space" do
36
+ launch_history = described_class.new_from_api("totalSecondsTracked " => 123)
37
+ expect(launch_history.total_seconds_tracked).to eq 123
38
+ end
39
+
40
+ it "is set properly when set in the root level without a trailing space" do
41
+ launch_history = described_class.new_from_api("totalSecondsTracked" => 123)
42
+ expect(launch_history.total_seconds_tracked).to eq 123
43
+ end
44
+
45
+ it "is set properly when set in the score object with a trailing space" do
46
+ launch_history = described_class.new_from_api("score" => { "totalSecondsTracked " => 123 })
47
+ expect(launch_history.total_seconds_tracked).to eq 123
48
+ end
49
+
50
+ it "is set properly when set in the score object without a trailing space" do
51
+ launch_history = described_class.new_from_api("score" => { "totalSecondsTracked" => 123 })
52
+ expect(launch_history.total_seconds_tracked).to eq 123
53
+ end
54
+
55
+ it "is set to zero when it's negative" do
56
+ launch_history = described_class.new_from_api("totalSecondsTracked" => -9.223372036854776e+16)
57
+ expect(launch_history.total_seconds_tracked).to eq 0
58
+ end
59
+
60
+ it "is left unset if not present" do
61
+ launch_history = described_class.new_from_api({})
62
+ expect(launch_history.total_seconds_tracked).to eq nil
63
+ end
64
+ end
65
+
66
+ describe ":instance_id" do
67
+ it "is coerced to an integer" do
68
+ expect(launch_history.instance_id).to eq 1
69
+ end
70
+ end
71
+
72
+ %i[launch_time exit_time last_runtime_update].each do |attr|
73
+ describe ":#{attr}" do
74
+ it "is set properly in the UTC timezone" do
75
+ expect(launch_history.send(attr)).to be_a Time
76
+ expect(launch_history.send(attr).zone).to eq "UTC"
77
+ end
78
+
79
+ it "is left unset if not present" do
80
+ launch_history = described_class.new_from_api({})
81
+ expect(launch_history.send(attr)).to eq nil
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,91 @@
1
+ RSpec.describe ScormEngine::Models::RegistrationRuntimeInteraction do
2
+ describe ".new_from_api" do
3
+ let(:options) { {
4
+ "id" => "interaction-123",
5
+ "description" => " foo \t bar \n baz ",
6
+ "timestampUtc" => "2018-05-24T00:01:02.00Z",
7
+ "correctResponses" => %w[one two],
8
+ "learnerResponse" => "one",
9
+ "result" => "correct",
10
+ "weighting" => "3",
11
+ "latency" => "0012:34:59"
12
+ } }
13
+
14
+ let(:interaction) { described_class.new_from_api(options) }
15
+
16
+ describe ":id" do
17
+ it "is set properly" do
18
+ expect(interaction.id).to eq "interaction-123"
19
+ end
20
+ end
21
+
22
+ describe ":description" do
23
+ it "is set properly" do
24
+ expect(interaction.description).to eq "foo bar baz"
25
+ end
26
+
27
+ it "is set to nil if blank" do
28
+ interaction = described_class.new_from_api(options.merge("description" => " "))
29
+ expect(interaction.description).to eq nil
30
+ end
31
+
32
+ it "is set to nil if literal value 'null'" do
33
+ interaction = described_class.new_from_api(options.merge("description" => "null"))
34
+ expect(interaction.description).to eq nil
35
+ end
36
+ end
37
+
38
+ describe ":timestamp" do
39
+ it "is set properly" do
40
+ expect(interaction.timestamp.iso8601).to eq "2018-05-24T00:01:02Z"
41
+ end
42
+
43
+ it "is set to nil if blank" do
44
+ interaction = described_class.new_from_api(options.merge("timestampUtc" => ""))
45
+ expect(interaction.timestamp).to eq nil
46
+ end
47
+ end
48
+
49
+ describe ":correct_responses" do
50
+ it "is set properly" do
51
+ expect(interaction.correct_responses).to eq %w[one two]
52
+ end
53
+ end
54
+
55
+ describe ":learner_response" do
56
+ it "is set properly" do
57
+ expect(interaction.learner_response).to eq "one"
58
+ end
59
+ end
60
+
61
+ describe ":result" do
62
+ it "is set properly" do
63
+ expect(interaction.result).to eq "correct"
64
+ end
65
+ end
66
+
67
+ describe ":weighting" do
68
+ it "is set properly" do
69
+ expect(interaction.weighting).to eq "3"
70
+ end
71
+ end
72
+
73
+ describe ":latency" do
74
+ it "is set properly" do
75
+ expect(interaction.latency).to eq "0012:34:59"
76
+ end
77
+ end
78
+
79
+ describe "#latency_in_seconds" do
80
+ it "parses valid values" do
81
+ interaction = described_class.new_from_api("latency" => "0012:34:59")
82
+ expect(interaction.latency_in_seconds).to eq 45_299
83
+ end
84
+
85
+ it "returns zero for invalid values" do
86
+ interaction = described_class.new_from_api("latency" => "wat")
87
+ expect(interaction.latency_in_seconds).to eq 0
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,82 @@
1
+ RSpec.describe ScormEngine::Models::Registration do
2
+ describe ".new_from_api" do
3
+ let(:options) { {
4
+ "id" => "registration-456",
5
+ "score" => {
6
+ "scaled" => "3.14159",
7
+ "additionalProperties" => {},
8
+ },
9
+ "course" => {
10
+ "id" => "course-789",
11
+ "title" => "Golf 101",
12
+ },
13
+ "learner" => {
14
+ "id" => "learner-123",
15
+ "firstName" => "Bobby",
16
+ "lastName" => "Jones",
17
+ },
18
+ } }
19
+
20
+ let(:registration) { described_class.new_from_api(options) }
21
+
22
+ describe ":id" do
23
+ it "is set properly" do
24
+ expect(registration.id).to eq "registration-456"
25
+ end
26
+ end
27
+
28
+ describe ":score" do
29
+ it "is set properly" do
30
+ expect(registration.score).to be_a Numeric
31
+ expect(registration.score.round(2)).to eq 3.14
32
+ end
33
+
34
+ it "is left unset if not present" do
35
+ registration = described_class.new_from_api({})
36
+ expect(registration.score).to eq nil
37
+ end
38
+ end
39
+
40
+ describe ":completed_date" do
41
+ it "is set properly when key is in root level" do
42
+ registration = described_class.new_from_api(options.merge("completedDate" => "2018-05-24T00:01:02.000Z"))
43
+ expect(registration.completed_date).to be_a Time
44
+ expect(registration.completed_date.iso8601).to eq "2018-05-24T00:01:02Z"
45
+ end
46
+
47
+ it "is set properly when key is in score object" do
48
+ registration = described_class.new_from_api(options.merge("score" => { "completedDate" => "2018-05-24T00:01:02.000Z" }))
49
+ expect(registration.completed_date).to be_a Time
50
+ expect(registration.completed_date.iso8601).to eq "2018-05-24T00:01:02Z"
51
+ end
52
+
53
+ it "is left unset if not present" do
54
+ expect(registration.completed_date).to eq nil
55
+ end
56
+ end
57
+
58
+ describe ":course" do
59
+ it "is set properly" do
60
+ expect(registration.course).to be_a ScormEngine::Models::Course
61
+ expect(registration.course.id).to eq "course-789"
62
+ end
63
+
64
+ it "is left unset if not present" do
65
+ registration = described_class.new_from_api({})
66
+ expect(registration.course).to eq nil
67
+ end
68
+ end
69
+
70
+ describe ":learner" do
71
+ it "is set properly" do
72
+ expect(registration.learner).to be_a ScormEngine::Models::Learner
73
+ expect(registration.learner.id).to eq "learner-123"
74
+ end
75
+
76
+ it "is left unset if not present" do
77
+ registration = described_class.new_from_api({})
78
+ expect(registration.learner).to eq nil
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,15 @@
1
+ RSpec.describe ScormEngine::Utils do
2
+ describe ".sanitized_text" do
3
+ it "removes unescaped HTML" do
4
+ expect(described_class.sanitized_text("<b class='world'>Hello</b>")).to eq "Hello"
5
+ end
6
+
7
+ it "strips and squeezes all white space" do
8
+ expect(described_class.sanitized_text(" a b\n\r\t c ")).to eq "a b c"
9
+ end
10
+
11
+ pending "encodes string to utf8"
12
+ pending "replaces invalid utf8"
13
+ pending "replaces undefined utf8"
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.describe ScormEngine do
2
+ it "has a version number" do
3
+ expect(ScormEngine::VERSION).not_to be nil
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ RSpec.describe ScormEngine do
2
+ end
@@ -0,0 +1,47 @@
1
+ require "bundler/setup"
2
+ require "dotenv"
3
+ require "pry"
4
+ require "scorm_engine"
5
+
6
+ # Ensure we're picking up only the test scorm settings
7
+ Dotenv.load(".env.test")
8
+
9
+ RSpec.configure do |config|
10
+ # Enable flags like --only-failures and --next-failure
11
+ config.example_status_persistence_file_path = ".rspec_status"
12
+
13
+ # Disable RSpec exposing methods globally on `Module` and `main`
14
+ config.disable_monkey_patching!
15
+
16
+ # rspec-expectations config goes here. You can use an alternate
17
+ # assertion/expectation library such as wrong or the stdlib/minitest
18
+ # assertions if you prefer.
19
+ config.expect_with :rspec do |expectations|
20
+ # Enable only the newer, non-monkey-patching expect syntax.
21
+ # For more details, see:
22
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
23
+ expectations.syntax = :expect
24
+ end
25
+
26
+ # Run specs in random order to surface order dependencies. If you find an
27
+ # order dependency and want to debug it, you can fix the order by providing
28
+ # the seed, which is printed after each run.
29
+ # --seed 1234
30
+ config.order = :random
31
+
32
+ # Seed global randomization in this process using the `--seed` CLI option.
33
+ # Setting this allows you to use `--seed` to deterministically reproduce
34
+ # test failures related to randomization by passing the same `--seed` value
35
+ # as the one that triggered the failure.
36
+ Kernel.srand config.seed
37
+
38
+ # Tag all groups and examples to use VCR
39
+ config.define_derived_metadata do |metadata|
40
+ metadata[:vcr] = true
41
+ end
42
+ end
43
+
44
+ # Require every spec support file
45
+ Dir[File.join(File.dirname(__FILE__), "support", "**/*.rb")].each do |file|
46
+ require file
47
+ end
@@ -0,0 +1,72 @@
1
+ module ScormEngineHelpers
2
+ #
3
+ # If a real scorm engine is available this method will disable VCR and
4
+ # execute the passed block. It is useful for when we need to ensure certain
5
+ # records are present in SCORM engine, but we do not want them recorded as
6
+ # the act of recording would break later expectations. That is if we record
7
+ # importing a course and play it back other tests that expect the actual
8
+ # record to exist in SCORM will fail.
9
+ #
10
+ def against_real_scorm_engine
11
+ return unless scorm_engine_is_available?
12
+
13
+ cassette = VCR.eject_cassette
14
+ VCR.turned_off do
15
+ yield
16
+ end
17
+ VCR.insert_cassette(cassette.name, cassette.instance_variable_get(:@options))
18
+ end
19
+
20
+ #
21
+ # Check to see if a scorm engine is available. Probably true while developing
22
+ # locally, definitely not true in Travis.
23
+ #
24
+ def scorm_engine_is_available?
25
+ ENV["SCORM_ENGINE_IS_AVAILABLE"] == "true"
26
+ end
27
+
28
+ #
29
+ # Ensure that the specified course exists in SCORM engine. Will import course
30
+ # if not present.
31
+ #
32
+ def ensure_course_exists(options = {})
33
+ response = options[:client].get_courses(course_id: options[:course_id])
34
+ return if response&.results&.first&.id == options[:course_id]
35
+ import_course(options)
36
+ end
37
+
38
+ #
39
+ # Attempt to import a course to SCORM engine.
40
+ #
41
+ def import_course(options = {})
42
+ options = { key: "RuntimeBasicCalls_SCORM20043rdEdition", may_create_new_version: true }.merge(options)
43
+
44
+ url = "https://github.com/phallstrom/scorm_engine/raw/master/spec/fixtures/zip/#{options[:key]}.zip"
45
+
46
+ import = options[:client].post_course_import(course_id: options[:course_id], url: url, may_create_new_version: options[:may_create_new_version])
47
+
48
+ if import.success?
49
+ loop do
50
+ import = options[:client].get_course_import(id: import.result.id)
51
+ break unless import.success? && import.result.running?
52
+ sleep 5
53
+ end
54
+ end
55
+
56
+ import
57
+ end
58
+
59
+ #
60
+ # Ensure that the specified registration exists in SCORM engine. Course
61
+ # must exist first.
62
+ #
63
+ def ensure_registration_exists(options = {})
64
+ response = options[:client].get_registration_exists(registration_id: options[:registration_id])
65
+ return if response&.result
66
+ options[:client].post_registration(options)
67
+ end
68
+ end
69
+
70
+ RSpec.configure do |c|
71
+ c.include ScormEngineHelpers
72
+ end