mumuki-laboratory 7.6.2 → 7.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +203 -2
  3. data/Rakefile +9 -0
  4. data/app/assets/javascripts/mumuki_laboratory/application.js +0 -1
  5. data/app/assets/javascripts/mumuki_laboratory/application/alias-modes.js +1 -1
  6. data/app/assets/javascripts/mumuki_laboratory/application/assets-loader.js +1 -1
  7. data/app/assets/javascripts/mumuki_laboratory/application/bridge.js +82 -47
  8. data/app/assets/javascripts/mumuki_laboratory/application/button.js +90 -1
  9. data/app/assets/javascripts/mumuki_laboratory/application/codemirror-builder.js +28 -25
  10. data/app/assets/javascripts/mumuki_laboratory/application/codemirror.js +8 -9
  11. data/app/assets/javascripts/mumuki_laboratory/application/confirmation.js +2 -2
  12. data/app/assets/javascripts/mumuki_laboratory/application/console.js +41 -43
  13. data/app/assets/javascripts/mumuki_laboratory/application/csrf-token.js +9 -12
  14. data/app/assets/javascripts/mumuki_laboratory/application/custom-editor.js +46 -8
  15. data/app/assets/javascripts/mumuki_laboratory/application/discussions.js +15 -16
  16. data/app/assets/javascripts/mumuki_laboratory/application/editors.js +104 -0
  17. data/app/assets/javascripts/mumuki_laboratory/application/elipsis.js +5 -4
  18. data/app/assets/javascripts/mumuki_laboratory/application/exercise.js +32 -0
  19. data/app/assets/javascripts/mumuki_laboratory/application/inputs.js +4 -2
  20. data/app/assets/javascripts/mumuki_laboratory/application/interval.js +2 -4
  21. data/app/assets/javascripts/mumuki_laboratory/application/kids.js +74 -37
  22. data/app/assets/javascripts/mumuki_laboratory/application/load-analytics.js +1 -1
  23. data/app/assets/javascripts/mumuki_laboratory/application/load-error-svg.js +1 -1
  24. data/app/assets/javascripts/mumuki_laboratory/application/messages.js +2 -2
  25. data/app/assets/javascripts/mumuki_laboratory/application/multiple-choice.js +1 -1
  26. data/app/assets/javascripts/mumuki_laboratory/application/multiple-scenarios.js +3 -6
  27. data/app/assets/javascripts/mumuki_laboratory/application/pin.js +3 -5
  28. data/app/assets/javascripts/mumuki_laboratory/application/progress.js +27 -6
  29. data/app/assets/javascripts/mumuki_laboratory/application/results-renderer.js +60 -0
  30. data/app/assets/javascripts/mumuki_laboratory/application/speech-bubble-renderer.js +12 -5
  31. data/app/assets/javascripts/mumuki_laboratory/application/submission.js +122 -55
  32. data/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js +93 -0
  33. data/app/assets/javascripts/mumuki_laboratory/application/sync-mode.js +75 -0
  34. data/app/assets/javascripts/mumuki_laboratory/application/timer.js +5 -6
  35. data/app/assets/javascripts/mumuki_laboratory/application/tooltip.js +1 -1
  36. data/app/assets/javascripts/mumuki_laboratory/application/upload.js +1 -1
  37. data/app/assets/javascripts/mumuki_laboratory/application/user.js +1 -1
  38. data/app/assets/stylesheets/mumuki_laboratory/application/_modules.scss +1 -0
  39. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +43 -5
  40. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_gs-board.scss +3 -0
  41. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kids.scss +3 -4
  42. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kindergarten.scss +55 -0
  43. data/app/controllers/application_controller.rb +1 -0
  44. data/app/controllers/assets_controller.rb +2 -0
  45. data/app/controllers/concerns/with_authorization.rb +4 -0
  46. data/app/controllers/concerns/with_user_discussion_validation.rb +14 -0
  47. data/app/controllers/discussions_controller.rb +6 -14
  48. data/app/controllers/discussions_messages_controller.rb +10 -1
  49. data/app/controllers/exercise_solutions_controller.rb +4 -2
  50. data/app/helpers/application_helper.rb +9 -5
  51. data/app/helpers/discussions_helper.rb +37 -23
  52. data/app/helpers/exercise_input_helper.rb +1 -1
  53. data/app/helpers/{locale_helper.rb → globals_helper.rb} +6 -2
  54. data/app/helpers/icons_helper.rb +3 -3
  55. data/app/mailers/user_mailer.rb +24 -11
  56. data/app/views/book/show.html.erb +1 -1
  57. data/app/views/book_discussions/index.html.erb +3 -3
  58. data/app/views/discussions/_message.html.erb +20 -8
  59. data/app/views/discussions/index.html.erb +0 -1
  60. data/app/views/discussions/new.html.erb +33 -0
  61. data/app/views/discussions/show.html.erb +18 -46
  62. data/app/views/exercise_solutions/_contextualization_results_container.html.erb +1 -1
  63. data/app/views/exercise_solutions/_results_title.html.erb +2 -2
  64. data/app/views/exercises/_read_only.html.erb +33 -6
  65. data/app/views/exercises/show.html.erb +2 -0
  66. data/app/views/layouts/_copyright.html.erb +1 -1
  67. data/app/views/layouts/_discussions.html.erb +21 -3
  68. data/app/views/layouts/_main.html.erb +1 -2
  69. data/app/views/layouts/_progress.html.erb +1 -1
  70. data/app/views/layouts/_progress_bar.html.erb +7 -1
  71. data/app/views/layouts/_test_results.html.erb +1 -1
  72. data/app/views/layouts/application.html.erb +1 -1
  73. data/app/views/layouts/exercise_inputs/editors/_custom.html.erb +1 -1
  74. data/app/views/layouts/exercise_inputs/forms/_kids_form.html.erb +1 -1
  75. data/app/views/layouts/exercise_inputs/forms/_problem_form.html.erb +1 -1
  76. data/app/views/layouts/exercise_inputs/layouts/_input_bottom.html.erb +1 -1
  77. data/app/views/layouts/exercise_inputs/layouts/_input_kindergarten.html.erb +40 -0
  78. data/app/views/layouts/exercise_inputs/layouts/{_input_kids.html.erb → _input_primary.html.erb} +1 -1
  79. data/app/views/layouts/exercise_inputs/layouts/_input_right.html.erb +1 -1
  80. data/app/views/layouts/modals/_kids_context.html.erb +1 -8
  81. data/app/views/user_mailer/1st_reminder.html.erb +1 -1
  82. data/app/views/user_mailer/1st_reminder.text.erb +1 -1
  83. data/app/views/user_mailer/2nd_reminder.html.erb +1 -1
  84. data/app/views/user_mailer/2nd_reminder.text.erb +1 -1
  85. data/app/views/user_mailer/3rd_reminder.html.erb +1 -1
  86. data/app/views/user_mailer/3rd_reminder.text.erb +1 -1
  87. data/app/views/user_mailer/no_submissions_reminder.html.erb +1 -1
  88. data/app/views/user_mailer/no_submissions_reminder.text.erb +1 -1
  89. data/config/routes.rb +2 -1
  90. data/lib/mumuki/laboratory/controllers.rb +1 -0
  91. data/lib/mumuki/laboratory/controllers/incognito_mode.rb +28 -0
  92. data/lib/mumuki/laboratory/controllers/results_rendering.rb +1 -2
  93. data/lib/mumuki/laboratory/locales/en.yml +14 -6
  94. data/lib/mumuki/laboratory/locales/es-CL.yml +292 -0
  95. data/lib/mumuki/laboratory/locales/es.yml +13 -5
  96. data/lib/mumuki/laboratory/locales/pt.yml +12 -6
  97. data/lib/mumuki/laboratory/version.rb +1 -1
  98. data/spec/controllers/confirmations_controller_spec.rb +1 -1
  99. data/spec/controllers/discussions_messages_controller_spec.rb +73 -0
  100. data/spec/controllers/exercise_solutions_controller_spec.rb +41 -6
  101. data/spec/dummy/db/schema.rb +13 -1
  102. data/spec/features/chapter_spec.rb +17 -0
  103. data/spec/features/discussion_flow_spec.rb +190 -0
  104. data/spec/features/exercise_flow_spec.rb +48 -3
  105. data/spec/features/home_public_flow_spec.rb +16 -0
  106. data/spec/features/menu_bar_spec.rb +88 -7
  107. data/spec/helpers/breadcrumbs_helper_spec.rb +1 -1
  108. data/spec/javascripts/bridge-spec.js +5 -0
  109. data/spec/javascripts/csrf-token-spec.js +7 -0
  110. data/spec/javascripts/editors-spec.js +54 -0
  111. data/spec/javascripts/elipsis-spec.js +25 -0
  112. data/spec/javascripts/exercise-spec.js +22 -0
  113. data/spec/javascripts/global-spec.js +6 -0
  114. data/spec/javascripts/results-renderers-spec.js +17 -0
  115. data/spec/javascripts/spec-helper.js +34 -0
  116. data/spec/javascripts/speech-bubble-renderer-spec.js +11 -0
  117. data/spec/javascripts/submissions-store-spec.js +44 -0
  118. data/spec/javascripts/sync-mode-spec.js +15 -0
  119. data/spec/javascripts/timeout-spec.js +5 -0
  120. data/spec/javascripts/timer-spec.js +5 -0
  121. data/spec/mailers/user_mailer_spec.rb +18 -3
  122. data/spec/teaspoon_env.rb +193 -0
  123. metadata +50 -11
  124. data/app/helpers/version_helper.rb +0 -5
  125. data/app/views/layouts/modals/_new_discussion.html.erb +0 -27
  126. data/vendor/assets/javascripts/hotjar.js +0 -8
@@ -91,4 +91,20 @@ feature 'public org', organization_workspace: :test do
91
91
  expect(user.reload.last_organization).to eq current_organization
92
92
  end
93
93
  end
94
+
95
+ context 'incognito user' do
96
+ before { current_organization.update! incognito_mode_enabled: true }
97
+ scenario 'from inside' do
98
+ Capybara.current_session.driver.header 'Referer', 'http://google.com'
99
+
100
+ visit '/'
101
+
102
+ expect(page).to have_text('ム mumuki')
103
+ expect(page).to have_text('First Steps')
104
+ expect(page).to have_selector('.chapter', text: 'Functional Programming')
105
+ expect(page).to have_text(current_organization.book.name)
106
+
107
+ expect(page).to have_text('Sign in')
108
+ end
109
+ end
94
110
  end
@@ -1,7 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  feature 'menu bar' do
4
- let(:chapter) { create(:chapter, lessons: [create(:lesson)]) }
4
+ let(:lesson) { create(:lesson, exercises: create_list(:exercise, 3))}
5
+ let(:chapter) { create(:chapter, lessons: [lesson]) }
5
6
  let(:book) { create(:book, chapters: [chapter], name: 'private', slug: 'mumuki/mumuki-the-private-book') }
6
7
  let(:private_organization) { create(:organization, name: 'private', book: book) }
7
8
 
@@ -32,6 +33,8 @@ feature 'menu bar' do
32
33
  expect(page).not_to have_text('Profile')
33
34
  expect(page).not_to have_text('Classroom')
34
35
  expect(page).not_to have_text('Bibliotheca')
36
+ expect(page).not_to have_text('Solve other\'s doubts')
37
+ expect(page).not_to have_text('My doubts')
35
38
  end
36
39
  end
37
40
  end
@@ -52,15 +55,91 @@ feature 'menu bar' do
52
55
  expect(page).to have_text('Profile')
53
56
  expect(page).not_to have_text('Classroom')
54
57
  expect(page).not_to have_text('Bibliotheca')
58
+ expect(page).not_to have_text('Solve other\'s doubts')
59
+ expect(page).not_to have_text('My doubts')
55
60
  end
56
61
 
57
- scenario 'student should only see profile' do
58
- set_current_user! visitor
62
+ context 'student with no discussions should' do
63
+ scenario 'only see profile if forum is not enabled' do
64
+ set_current_user! student
59
65
 
60
- visit '/'
61
- expect(page).to have_text('Profile')
62
- expect(page).not_to have_text('Classroom')
63
- expect(page).not_to have_text('Bibliotheca')
66
+ visit '/'
67
+ expect(page).to have_text('Profile')
68
+ expect(page).not_to have_text('Classroom')
69
+ expect(page).not_to have_text('Bibliotheca')
70
+ expect(page).not_to have_text('Solve other\'s doubts')
71
+ expect(page).not_to have_text('My doubts')
72
+ end
73
+
74
+ scenario 'see profile and solve_other_doubts links if forum is enabled' do
75
+ set_current_user! student
76
+ private_organization.update! forum_enabled: true
77
+
78
+ visit '/'
79
+ expect(page).to have_text('Profile')
80
+ expect(page).not_to have_text('Classroom')
81
+ expect(page).not_to have_text('Bibliotheca')
82
+ expect(page).to have_text('Solve other\'s doubts')
83
+ expect(page).not_to have_text('My doubts')
84
+ end
85
+ end
86
+
87
+ context 'student with discussions should' do
88
+ let(:discussion) { create(:discussion, item: lesson.exercises.last, initiator: student)}
89
+
90
+ scenario 'only see profile if forum is not enabled' do
91
+ set_current_user! student
92
+ student.subscribe_to! discussion
93
+
94
+ visit '/'
95
+ expect(page).to have_text('Profile')
96
+ expect(page).not_to have_text('Classroom')
97
+ expect(page).not_to have_text('Bibliotheca')
98
+ expect(page).not_to have_text('Solve other\'s doubts')
99
+ expect(page).not_to have_text('My doubts')
100
+ end
101
+
102
+ scenario 'see all discussions links if forum is enabled' do
103
+ set_current_user! student
104
+ private_organization.update! forum_enabled: true
105
+ student.subscribe_to! discussion
106
+
107
+ visit '/'
108
+ expect(page).to have_text('Profile')
109
+ expect(page).not_to have_text('Classroom')
110
+ expect(page).not_to have_text('Bibliotheca')
111
+ expect(page).to have_text('Solve other\'s doubts')
112
+ expect(page).to have_text('My doubts')
113
+ end
114
+
115
+ scenario 'only see profile if forum is enabled in a forum_only_for_trusted organization' do
116
+ set_current_user! student
117
+ student.subscribe_to! discussion
118
+ private_organization.update! forum_enabled: true
119
+ private_organization.update! forum_only_for_trusted: true
120
+
121
+ visit '/'
122
+ expect(page).to have_text('Profile')
123
+ expect(page).not_to have_text('Classroom')
124
+ expect(page).not_to have_text('Bibliotheca')
125
+ expect(page).not_to have_text('Solve other\'s doubts')
126
+ expect(page).not_to have_text('My doubts')
127
+ end
128
+
129
+ scenario 'see all discussions links if forum is enabled in a forum_only_for_trusted organization but it is trusted' do
130
+ student.update! trusted_for_forum: true
131
+ set_current_user! student
132
+ student.subscribe_to! discussion
133
+ private_organization.update! forum_enabled: true
134
+ private_organization.update! forum_only_for_trusted: true
135
+
136
+ visit '/'
137
+ expect(page).to have_text('Profile')
138
+ expect(page).not_to have_text('Classroom')
139
+ expect(page).not_to have_text('Bibliotheca')
140
+ expect(page).to have_text('Solve other\'s doubts')
141
+ expect(page).to have_text('My doubts')
142
+ end
64
143
  end
65
144
 
66
145
  scenario 'teacher should see profile and classroom' do
@@ -71,6 +150,8 @@ feature 'menu bar' do
71
150
  expect(page).to have_text('Profile')
72
151
  expect(page).to have_text('Classroom')
73
152
  expect(page).not_to have_text('Bibliotheca')
153
+ expect(page).not_to have_text('Solve other\'s doubts')
154
+ expect(page).not_to have_text('My doubts')
74
155
  end
75
156
 
76
157
  scenario 'writer should see profile and bibliotheca' do
@@ -91,7 +91,7 @@ describe BreadcrumbsHelper, organization_workspace: :test do
91
91
  expect(breadcrumb).to include('my lesson')
92
92
  expect(breadcrumb).to include('my exercise')
93
93
  expect(breadcrumb).to include('discussions')
94
- expect(breadcrumb).to include(discussion.title)
94
+ expect(breadcrumb).to include(discussion.item.navigable_name)
95
95
  expect(breadcrumb).to be_html_safe
96
96
  end
97
97
  end
@@ -0,0 +1,5 @@
1
+ describe('bridge', () => {
2
+ it('can create bridge', () => {
3
+ expect(new mumuki.bridge.Laboratory()).not.toBe(null);
4
+ })
5
+ })
@@ -0,0 +1,7 @@
1
+ describe('csrf token', () => {
2
+ it('can create token', () => {
3
+ expect(new mumuki.CsrfToken()).not.toBe(null);
4
+ })
5
+ })
6
+
7
+
@@ -0,0 +1,54 @@
1
+ describe('editors', () => {
2
+
3
+ beforeEach(() => {
4
+ mumuki.CustomEditor.clearSources()
5
+ })
6
+
7
+ it('has initially no sources', () => {
8
+ expect(mumuki.CustomEditor.hasSources).toBe(false)
9
+ });
10
+
11
+ it('can add a custom source', () => {
12
+ mumuki.editors.addCustomSource({
13
+ getContent() {
14
+ return { name: "solution[content]", value: 'the value' } ;
15
+ }
16
+ });
17
+
18
+ expect(mumuki.CustomEditor.hasSources).toBe(true);
19
+ expect(mumuki.CustomEditor.getContents()[0].value).toEqual('the value');
20
+ expect(mumuki.CustomEditor.getContents()[0].name).toEqual('solution[content]');
21
+ });
22
+
23
+ it('reads the custom sources if present, ignoring the form', () => {
24
+ $('body').html(`
25
+ <form role="form" class="new_solution">
26
+ <div class="field form-group editor-code">
27
+ <textarea class="form-control editor" name="solution[content]" id="solution_content">the standard solution</textarea>
28
+ </div>
29
+ </form>`)
30
+
31
+ mumuki.editors.addCustomSource({
32
+ getContent() {
33
+ return { name: "solution[content]", value: 'the custom solution' } ;
34
+ }
35
+ });
36
+
37
+ expect(mumuki.editors.getSubmission()).toEqual({"solution[content]":"the custom solution"});
38
+ });
39
+
40
+ it('reads the form if no sources', () => {
41
+ $('body').html(`
42
+ <form role="form" class="new_solution">
43
+ <div class="field form-group editor-code">
44
+ <textarea class="form-control editor" name="solution[content]" id="solution_content">the solution</textarea>
45
+ </div>
46
+ </form>`)
47
+ expect(mumuki.editors.getSubmission()).toEqual({"solution[content]":"the solution"});
48
+ });
49
+
50
+ it('produces empty submission if no form nor sources', () => {
51
+ $('body').html(``);
52
+ expect(mumuki.editors.getSubmission()).toEqual({});
53
+ });
54
+ })
@@ -0,0 +1,25 @@
1
+ describe('elipsis', () => {
2
+ it('does nothing when no elipsis', () => {
3
+ expect(mumuki.elipsis('hello')).toEqual('hello');
4
+ })
5
+
6
+ it('replaces student elipsis', () => {
7
+ expect(mumuki.elipsis(`function longitud(unString) {
8
+ /*&lt;elipsis-for-student@*/
9
+ return unString.length;
10
+ /*@elipsis-for-student&gt;*/
11
+ }`)).toEqual(`function longitud(unString) {
12
+ /* ... */
13
+ }`)
14
+ });
15
+
16
+ it('replaces student hidden', () => {
17
+ expect(mumuki.elipsis(`function longitud(unString) {
18
+ /*&lt;hidden-for-student@*/
19
+ return unString.length;
20
+ /*@hidden-for-student&gt;*/
21
+ }`)).toEqual(`function longitud(unString) {
22
+ /**/
23
+ }`)
24
+ });
25
+ })
@@ -0,0 +1,22 @@
1
+ describe('exercise', () => {
2
+ it('current exercise information is available when present', () => {
3
+ $('body').html(`
4
+ <input type="hidden" name="mu-exercise-id" id="mu-exercise-id" value="3361" />
5
+ <input type="hidden" name="mu-exercise-layout" id="mu-exercise-layout" value="input_right" />`)
6
+
7
+ mumuki.exercise.load()
8
+
9
+ expect(mumuki.exercise.id).toBe(3361);
10
+ expect(mumuki.exercise.layout).toBe('input_right');
11
+ })
12
+
13
+ it('current exercise information is available when not present', () => {
14
+ $('body').html(``)
15
+
16
+ mumuki.exercise.load()
17
+
18
+ expect(mumuki.exercise.id).toBe(null);
19
+ expect(mumuki.exercise.layout).toBe(null);
20
+ })
21
+
22
+ })
@@ -0,0 +1,6 @@
1
+ describe("global loading", () => {
2
+ it("produces no global loading errors", () => {
3
+ const error = window['__globalLoadingError__'];
4
+ expect(!error).toBe(true, `Expected no global loading errors but got ${error && error.message}`)
5
+ })
6
+ })
@@ -0,0 +1,17 @@
1
+ describe('results renderers', () => {
2
+ it('can compute class for status', () => {
3
+ expect(mumuki.renderers.classForStatus('passed')).toEqual('success');
4
+ })
5
+
6
+ it('can compute icon for status', () => {
7
+ expect(mumuki.renderers.iconForStatus('pending')).toEqual('fa-circle');
8
+ })
9
+
10
+ it('can compute progress list item for status', () => {
11
+ expect(mumuki.renderers.progressListItemClassForStatus('passed_with_warnings')).toEqual('progress-list-item text-center warning ');
12
+ })
13
+
14
+ it('can compute progress list item for status when active', () => {
15
+ expect(mumuki.renderers.progressListItemClassForStatus('failed', true)).toEqual('progress-list-item text-center danger active');
16
+ })
17
+ })
@@ -0,0 +1,34 @@
1
+ // Teaspoon includes some support files, but you can use anything from your own support path too.
2
+ // require support/jasmine-jquery-1.7.0
3
+ // require support/jasmine-jquery-2.0.0
4
+ // require support/jasmine-jquery-2.1.0
5
+ // require support/sinon
6
+ //
7
+ // PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion.
8
+ // Use this polyfill to avoid the confusion.
9
+ //
10
+ // You can require your own javascript files here. By default this will include everything in application, however you
11
+ // may get better load performance if you require the specific files that are being used in the spec that tests them.
12
+ //=require mumuki_laboratory/application
13
+ //
14
+ // Deferring execution
15
+ // If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call
16
+ // Teaspoon.execute() after everything has been loaded. Simple example of a timeout:
17
+ //
18
+ // Teaspoon.defer = true
19
+ // setTimeout(Teaspoon.execute, 1000)
20
+ //
21
+ // Matching files
22
+ // By default Teaspoon will look for files that match _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your
23
+ // spec path and it'll be included in the default suite automatically. If you want to customize suites, check out the
24
+ // configuration in teaspoon_env.rb
25
+ //
26
+ // Manifest
27
+ // If you'd rather require your spec files manually (to control order for instance) you can disable the suite matcher in
28
+ // the configuration and use this file as a manifest.
29
+ //
30
+ // For more information: http://github.com/modeset/teaspoon
31
+
32
+ window.addEventListener("error", (e) => {
33
+ window['__globalLoadingError__'] = e;
34
+ });
@@ -0,0 +1,11 @@
1
+ describe('results renderers', () => {
2
+ it('can render result item', () => {
3
+ expect(mumuki.renderers.renderSpeechBubbleResultItem('fix that')).toEqual(`
4
+ <div class="results-item">
5
+ <ul class="results-list">
6
+ <li>fix that</li>
7
+ </ul>
8
+ </div>`);
9
+ })
10
+
11
+ })
@@ -0,0 +1,44 @@
1
+ describe("SubmissionsStore", () => {
2
+ const emptyProgramSubmission = {"solution[content]": "program {}"};
3
+ /** @type {SubmissionResult} */
4
+ const passedSubmissionResult = {status: 'passed'};
5
+ /** @type {SubmissionAndResult} */
6
+ const passedEmptyProgramSubmissionAndResult = { submission: emptyProgramSubmission, result: passedSubmissionResult };
7
+
8
+ beforeEach(() => {
9
+ window.localStorage.clear()
10
+ });
11
+
12
+ describe('getLastSubmission', () => {
13
+ it("answers null if submission not present", () => {
14
+ expect(mumuki.SubmissionsStore.getLastSubmissionAndResult(1)).toBe(null)
15
+ })
16
+
17
+ it("answers the last submission result if already sent", () => {
18
+ mumuki.SubmissionsStore.setSubmissionResultFor(1, passedEmptyProgramSubmissionAndResult)
19
+ expect(mumuki.SubmissionsStore.getLastSubmissionAndResult(1)).toEqual(passedEmptyProgramSubmissionAndResult)
20
+ })
21
+ })
22
+
23
+ describe('getLastSubmissionStatus', () => {
24
+ it("answers pending if submission not present", () => {
25
+ expect(mumuki.SubmissionsStore.getLastSubmissionStatus(1)).toBe('pending')
26
+ })
27
+
28
+ it("answers the last submission status if previously sent", () => {
29
+ mumuki.SubmissionsStore.setSubmissionResultFor(1, passedEmptyProgramSubmissionAndResult)
30
+ expect(mumuki.SubmissionsStore.getLastSubmissionStatus(1)).toBe('passed')
31
+ })
32
+ });
33
+
34
+ describe('getCachedResultFor', () => {
35
+ it("answers null if submission not present", () => {
36
+ expect(mumuki.SubmissionsStore.getSubmissionResultFor(1, emptyProgramSubmission)).toBe(null)
37
+ })
38
+
39
+ it("answers the last submission if previously sent", () => {
40
+ mumuki.SubmissionsStore.setSubmissionResultFor(1, passedEmptyProgramSubmissionAndResult)
41
+ expect(mumuki.SubmissionsStore.getSubmissionResultFor(1, emptyProgramSubmission)).toEqual(passedSubmissionResult)
42
+ })
43
+ });
44
+ })
@@ -0,0 +1,15 @@
1
+ describe('sync mode', () => {
2
+ it('can choose server mode', () => {
3
+ mumuki.incognitoUser = false;
4
+ mumuki.syncMode._selectSyncMode();
5
+
6
+ expect(mumuki.syncMode._current instanceof mumuki.syncMode.ServerSyncMode).toBe(true);
7
+ })
8
+
9
+ it('can choose local mode', () => {
10
+ mumuki.incognitoUser = true;
11
+ mumuki.syncMode._selectSyncMode();
12
+
13
+ expect(mumuki.syncMode._current instanceof mumuki.syncMode.ClientSyncMode).toBe(true);
14
+ })
15
+ })
@@ -0,0 +1,5 @@
1
+ describe('setTimeout', () => {
2
+ it('can create setTimeout', () => {
3
+ expect(mumuki.setTimeout).not.toBe(null);
4
+ })
5
+ })
@@ -0,0 +1,5 @@
1
+ describe('timer', () => {
2
+ it('can create timer', () => {
3
+ expect(mumuki.startTimer).not.toBe(null);
4
+ })
5
+ })
@@ -148,21 +148,21 @@ RSpec.describe UserMailer, type: :mailer do
148
148
 
149
149
  context "registered 1 week ago" do
150
150
  it { expect(user.should_remind?).to be true }
151
- it { expect(reminder.body.encoded).to include("you've never submitted solutions") }
151
+ it { expect(reminder.body.encoded).to include('you&#39;ve never submitted solutions') }
152
152
  end
153
153
 
154
154
  context "last submission 2 weeks ago" do
155
155
  let(:days_since_user_creation) { 16 }
156
156
 
157
157
  it { expect(user.should_remind?).to be true }
158
- it { expect(reminder.body.encoded).to include("you've never submitted solutions") }
158
+ it { expect(reminder.body.encoded).to include('you&#39;ve never submitted solutions') }
159
159
  end
160
160
 
161
161
  context "last submission 3 weeks ago" do
162
162
  let(:days_since_user_creation) { 26 }
163
163
 
164
164
  it { expect(user.should_remind?).to be true }
165
- it { expect(reminder.body.encoded).to include("you've never submitted solutions") }
165
+ it { expect(reminder.body.encoded).to include('you&#39;ve never submitted solutions') }
166
166
  end
167
167
 
168
168
  context "last submission 4 weeks ago" do
@@ -178,4 +178,19 @@ RSpec.describe UserMailer, type: :mailer do
178
178
  it { expect(user.should_remind?).to be false }
179
179
  end
180
180
  end
181
+
182
+ describe 'welcome email' do
183
+ let(:email) { UserMailer.welcome_email(user, organization) }
184
+
185
+ let(:non_custom_welcome_orga) { create :organization }
186
+ let(:custom_welcome_orga) { create :organization, welcome_email_template: 'hello <%= @user.first_name %>!' }
187
+
188
+ let(:user) { create :user, first_name: 'some name' }
189
+
190
+ context 'when organization does have a custom welcome template' do
191
+ let(:organization) { custom_welcome_orga }
192
+
193
+ it { expect(email.body.encoded).to eq 'hello some name!' }
194
+ end
195
+ end
181
196
  end