mumuki-laboratory 6.1.5 → 6.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +5 -5
  2. data/app/assets/javascripts/application.js +1 -0
  3. data/app/assets/javascripts/application/codemirror-builder.js +11 -1
  4. data/app/assets/javascripts/application/codemirror.js +0 -1
  5. data/app/assets/javascripts/application/discussions.js +15 -2
  6. data/app/assets/javascripts/application/free-form.js +1 -1
  7. data/app/assets/javascripts/application/kids.js +3 -3
  8. data/app/assets/javascripts/application/load-error-svg.js +1 -1
  9. data/app/controllers/invitations_controller.rb +1 -1
  10. data/app/helpers/application_helper.rb +0 -22
  11. data/app/helpers/assets_helper.rb +12 -0
  12. data/app/helpers/assignment_result_helper.rb +1 -1
  13. data/app/helpers/assistance_box_helper.rb +9 -0
  14. data/app/helpers/editor_helper.rb +13 -0
  15. data/app/helpers/locale_helper.rb +11 -0
  16. data/app/helpers/menu_bar_helper.rb +13 -0
  17. data/app/helpers/multiple_file_editor_helper.rb +5 -0
  18. data/app/helpers/open_graph_helper.rb +12 -0
  19. data/app/helpers/page_title_helper.rb +11 -0
  20. data/app/views/exercise_solutions/_kids_results.html.erb +1 -1
  21. data/app/views/exercises/_read_only.html.erb +3 -1
  22. data/app/views/invitations/show.html.erb +1 -1
  23. data/app/views/layouts/_kids.html.erb +1 -1
  24. data/app/views/layouts/_main.html.erb +5 -20
  25. data/app/views/layouts/application.html.erb +2 -7
  26. data/app/views/layouts/exercise_inputs/editors/_multiple_files.html.erb +11 -12
  27. data/app/views/layouts/exercise_inputs/layouts/_input_kids.html.erb +1 -1
  28. data/app/views/layouts/exercise_inputs/read_only_editors/_code.html.erb +1 -1
  29. data/app/views/layouts/exercise_inputs/read_only_editors/_free_form.html.erb +1 -1
  30. data/app/views/layouts/exercise_inputs/read_only_editors/_multiple_files.html.erb +29 -0
  31. data/app/views/layouts/mailer.html.erb +1 -1
  32. data/app/views/layouts/modals/_kids_context.html.erb +1 -1
  33. data/config/initializers/form_builder.rb +4 -5
  34. data/lib/events.rb +1 -4
  35. data/lib/mumuki/laboratory/locales/en.yml +0 -1
  36. data/lib/mumuki/laboratory/locales/es.yml +0 -1
  37. data/lib/mumuki/laboratory/locales/pt.yml +0 -1
  38. data/lib/mumuki/laboratory/version.rb +1 -1
  39. data/public/character/kids/{yellow_context.svg → kibi_context.svg} +2 -2
  40. data/public/{amarillo.svg → kibi.svg} +2 -2
  41. data/public/{anim_amarillo.svg → kibi_animated.svg} +2 -2
  42. data/public/{amarillo_fracaso.svg → kibi_failure.svg} +2 -2
  43. data/public/{amarillo_exito.svg → kibi_success.svg} +2 -2
  44. data/public/{amarillo_exito_bailarin.svg → kibi_success_dancing.svg} +2 -2
  45. data/spec/capybara_helper.rb +4 -4
  46. data/spec/helpers/application_helper_spec.rb +0 -16
  47. data/spec/helpers/page_title_helper_spec.rb +19 -0
  48. data/spec/spec_helper.rb +2 -1
  49. data/vendor/assets/javascripts/codemirror-autorefresh.js +46 -0
  50. metadata +76 -163
  51. data/spec/factories/api_client_factory.rb +0 -18
  52. data/spec/factories/assignments_factory.rb +0 -7
  53. data/spec/factories/book_factory.rb +0 -7
  54. data/spec/factories/chapter_factory.rb +0 -16
  55. data/spec/factories/complement_factory.rb +0 -6
  56. data/spec/factories/course_factory.rb +0 -9
  57. data/spec/factories/discussion_factory.rb +0 -8
  58. data/spec/factories/exam_factory.rb +0 -9
  59. data/spec/factories/exercise_factory.rb +0 -73
  60. data/spec/factories/guide_factory.rb +0 -30
  61. data/spec/factories/invitation_factory.rb +0 -7
  62. data/spec/factories/lesson_factory.rb +0 -7
  63. data/spec/factories/login_settings_factory.rb +0 -5
  64. data/spec/factories/message_factory.rb +0 -11
  65. data/spec/factories/organization_factory.rb +0 -26
  66. data/spec/factories/topic_factory.rb +0 -9
  67. data/spec/factories/user_factory.rb +0 -8
  68. data/spec/models/api_client_spec.rb +0 -6
  69. data/spec/models/assignment_spec.rb +0 -191
  70. data/spec/models/book_import_spec.rb +0 -49
  71. data/spec/models/book_spec.rb +0 -114
  72. data/spec/models/course_spec.rb +0 -22
  73. data/spec/models/discussion_spec.rb +0 -174
  74. data/spec/models/event_generation_spec.rb +0 -189
  75. data/spec/models/event_publishing_spec.rb +0 -36
  76. data/spec/models/exam_spec.rb +0 -151
  77. data/spec/models/exercise_spec.rb +0 -494
  78. data/spec/models/guide_import_spec.rb +0 -300
  79. data/spec/models/guide_spec.rb +0 -83
  80. data/spec/models/interactive_spec.rb +0 -143
  81. data/spec/models/language_spec.rb +0 -56
  82. data/spec/models/lesson_spec.rb +0 -90
  83. data/spec/models/message_spec.rb +0 -87
  84. data/spec/models/navigation_spec.rb +0 -70
  85. data/spec/models/organization_spec.rb +0 -124
  86. data/spec/models/playground_spec.rb +0 -23
  87. data/spec/models/problem_spec.rb +0 -80
  88. data/spec/models/query_spec.rb +0 -21
  89. data/spec/models/question_spec.rb +0 -37
  90. data/spec/models/reading_spec.rb +0 -10
  91. data/spec/models/solution_spec.rb +0 -66
  92. data/spec/models/stats_spec.rb +0 -15
  93. data/spec/models/string_spec.rb +0 -9
  94. data/spec/models/topic_import_spec.rb +0 -42
  95. data/spec/models/usage_spec.rb +0 -77
  96. data/spec/models/user_changed_spec.rb +0 -35
  97. data/spec/models/user_spec.rb +0 -276
  98. data/spec/models/with_expectations_spec.rb +0 -56
@@ -1,36 +0,0 @@
1
- require 'spec_helper'
2
- require 'rspec/mocks'
3
-
4
- describe 'events' do
5
- let(:exercise) { create(:exercise) }
6
- let(:assignment) { create(:assignment, exercise: exercise) }
7
-
8
- before { organization.switch! }
9
- before { create(:chapter, lessons: [ create(:lesson, exercises: [ exercise ]) ]) }
10
- before { reindex_current_organization! }
11
-
12
- describe '#notify!' do
13
- let!(:organization) { create(:organization, name: 'pdep') }
14
- before { expect_any_instance_of(Mumukit::Nuntius::NotificationMode::Deaf).to receive(:notify!) }
15
- before { organization.switch! }
16
-
17
- it { expect { assignment.notify! }.to_not raise_error }
18
- end
19
-
20
- describe 'protect in central book' do
21
- let!(:organization) { create(:organization, name: 'central') }
22
- before { expect_any_instance_of(Mumukit::Nuntius::NotificationMode::Deaf).to receive(:notify!) }
23
- before { organization.switch! }
24
-
25
- it { expect { assignment.notify! }.to_not raise_error }
26
- end
27
-
28
- describe 'submit_solution!' do
29
- let!(:organization) { create(:organization, name: 'pdep') }
30
- before { expect_any_instance_of(Mumukit::Nuntius::NotificationMode::Deaf).to receive(:notify!) }
31
- before { organization.switch! }
32
- let(:user) { create(:user) }
33
-
34
- it { expect { exercise.submit_solution! user }.to_not raise_error }
35
- end
36
- end
@@ -1,151 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Exam, organization_workspace: :test do
4
- let(:user) { create(:user) }
5
- let(:other_user) { create(:user) }
6
-
7
- describe '#upsert' do
8
- let(:guide) { create(:guide) }
9
- let(:exam_json) { {eid: '1', slug: guide.slug, start_time: 5.minutes.ago, end_time: 10.minutes.since, duration: 150, language: 'haskell', name: 'foo', uids: [], organization: 'test'} }
10
- let!(:exam) { Exam.import_from_resource_h! exam_json }
11
- context 'when new exam and the guide is the same' do
12
- let(:guide2) { create(:guide) }
13
- let(:exam_json2) { {eid: '2', slug: guide.slug, start_time: 5.minutes.ago, end_time: 10.minutes.since, duration: 150, language: 'haskell', name: 'foo', uids: [], organization: 'test'} }
14
- let!(:exam2) { Exam.import_from_resource_h! exam_json2 }
15
- context 'and the organization is the same' do
16
- it { expect(Exam.count).to eq 1 }
17
- it { expect(Usage.where(item_id: guide.id, parent_item_id: exam.id).count).to eq 0 }
18
- it { expect(ExamAuthorization.where(exam_id: exam.id).count).to eq 0 }
19
- end
20
- end
21
-
22
- end
23
-
24
- describe '#validate_accessible_for!' do
25
- context 'not enabled' do
26
- let(:exam) { create(:exam, start_time: 5.minutes.since, end_time: 10.minutes.since) }
27
-
28
- it { expect(exam.enabled?).to be false }
29
-
30
- context 'not authorized' do
31
- it { expect { exam.validate_accessible_for! user }.to raise_error(Mumuki::Domain::ForbiddenError) }
32
- end
33
-
34
- context 'authorized' do
35
- it { expect(exam.enabled?).to be false }
36
- end
37
- end
38
-
39
- context 'enabled' do
40
- let(:exam) { create(:exam, start_time: 5.minutes.ago, end_time: 10.minutes.since) }
41
-
42
- it { expect(exam.enabled?).to be true }
43
-
44
- context 'not authorized' do
45
- it { expect { exam.validate_accessible_for! user }.to raise_error(Mumuki::Domain::ForbiddenError) }
46
- end
47
-
48
- context 'authorized' do
49
- before { exam.authorize! user }
50
-
51
- it { expect { exam.validate_accessible_for! user }.to_not raise_error }
52
- it { expect { exam.validate_accessible_for! other_user }.to raise_error(Mumuki::Domain::ForbiddenError) }
53
- end
54
-
55
- context 'import_from_json' do
56
- let(:user) { create(:user, uid: 'auth0|1') }
57
- let(:user2) { create(:user, uid: 'auth0|2') }
58
- let(:guide) { create(:guide) }
59
- let(:duration) { 150 }
60
- let(:exam_json) { {eid: '1', slug: guide.slug, start_time: 5.minutes.ago, end_time: 10.minutes.since, duration: duration, language: 'haskell', name: 'foo', uids: [user.uid], organization: 'test'} }
61
- before { Exam.import_from_resource_h! exam_json }
62
-
63
- context 'new exam' do
64
- it { expect(Exam.count).to eq 1 }
65
- it { expect { Exam.find_by(classroom_id: '1').validate_accessible_for! user }.to_not raise_error }
66
- it { expect(guide.usage_in_organization).to be_a Exam }
67
- end
68
-
69
- context 'new exam, no duration' do
70
- let(:duration) { nil }
71
- it { expect(guide.usage_in_organization).to be_a Exam }
72
- end
73
-
74
- context 'existing exam' do
75
- let(:exam_json2) { {eid: '1', slug: guide.slug, start_time: 5.minutes.ago, end_time: 10.minutes.since, duration: 150, language: 'haskell', name: 'foo', uids: [user2.uid], organization: 'test'} }
76
- before { Exam.import_from_resource_h! exam_json2 }
77
-
78
- it { expect(Exam.count).to eq 1 }
79
- it { expect { Exam.find_by(classroom_id: '1').validate_accessible_for! user }.to raise_error(Mumuki::Domain::ForbiddenError) }
80
- it { expect { Exam.find_by(classroom_id: '1').validate_accessible_for! user2 }.to_not raise_error }
81
- end
82
- end
83
-
84
- context 'real_end_time' do
85
- let(:user) { create(:user, uid: 'auth0|1') }
86
- let(:guide) { create(:guide) }
87
- let(:exam_json) { {eid: '1', slug: guide.slug, start_time: 5.minutes.ago, end_time: 10.minutes.since, duration: duration, language: 'haskell', name: 'foo', uids: [user.uid], organization: 'test'} }
88
- let(:exam) { Exam.import_from_resource_h! exam_json }
89
- before { exam.start! user }
90
-
91
- context 'with duration' do
92
- let(:duration) { 150 }
93
- it { expect(exam.real_end_time user).to eq(exam.end_time) }
94
- it { expect(exam.started? user).to be_truthy }
95
- end
96
-
97
- context 'with short duration' do
98
- let(:duration) { 3 }
99
- it { expect(exam.real_end_time user).to eq(exam.started_at(user) + 3.minutes) }
100
- it { expect(exam.started? user).to be_truthy }
101
- end
102
-
103
- context 'no duration' do
104
- let(:duration) { nil }
105
- it { expect(exam.real_end_time user).to eq(exam.end_time) }
106
- it { expect(exam.started? user).to be_truthy }
107
- end
108
- end
109
-
110
- context 'update exam does not change user started_at' do
111
- let(:user) { create(:user, uid: 'auth0|1') }
112
- let(:guide) { create(:guide) }
113
- let(:exam_json) { {eid: '1', slug: guide.slug, start_time: 5.minutes.ago, end_time: 10.minutes.since, duration: 150, language: 'haskell', name: 'foo', uids: [user.uid], organization: 'test'} }
114
- let(:exam) { Exam.import_from_resource_h! exam_json }
115
- before { exam.start! user }
116
- before { Exam.import_from_resource_h! exam_json.merge(organization: 'test') }
117
-
118
- it { expect(exam.started?(user)).to be true }
119
-
120
- end
121
-
122
- context 'create exam with non existing user' do
123
- let(:guide) { create(:guide) }
124
- let(:exam_json) { {eid: '1', slug: guide.slug, start_time: 5.minutes.ago, end_time: 10.minutes.since, duration: 150, language: 'haskell', name: 'foo', uids: [user.uid], organization: 'test'} }
125
- let(:exam) { Exam.import_from_resource_h! exam_json }
126
-
127
- it { expect { Exam.import_from_resource_h! exam_json.merge(organization: 'test') }.not_to raise_error }
128
-
129
- end
130
-
131
- context 'teacher does not start exams' do
132
- let(:teacher) { create(:user, uid: 'auth0|1') }
133
- let(:guide) { create(:guide) }
134
- let(:exam_json) { {eid: '1', slug: guide.slug, start_time: 5.minutes.ago, end_time: 10.minutes.since, duration: 150, language: 'haskell', name: 'foo', uids: [], organization: 'test'} }
135
- let(:exam) { Exam.import_from_resource_h! exam_json }
136
-
137
- context 'exam_authorization do not receive start method' do
138
- before { expect(teacher).to receive(:teacher_here?).and_return(true) }
139
- before { expect_any_instance_of(ExamAuthorization).to_not receive(:start!) }
140
- it { expect { exam.start!(teacher) }.to_not raise_error }
141
-
142
- end
143
-
144
- context 'exam is not started by a teacher' do
145
- it { expect(exam.started?(teacher)).to be_falsey }
146
- end
147
-
148
- end
149
- end
150
- end
151
- end
@@ -1,494 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Exercise, organization_workspace: :test do
4
- let(:exercise) { create(:exercise) }
5
- let(:user) { create(:user, first_name: 'Orlo') }
6
-
7
- describe '#choice_values' do
8
- context 'when choices are in 5.0 format' do
9
- let(:choice_values) { %w(1492 1453 1773) }
10
- let(:exercise) { build(:exercise, description: 'when did byzantine empire fall?', choice_values: choice_values) }
11
-
12
- it { expect(exercise.choices).to be_blank }
13
- it { expect(exercise[:choice_values]).to eq choice_values }
14
- it { expect(exercise.choice_values).to eq choice_values }
15
- it { expect(exercise.choice_index_for '1492').to eq 0 }
16
- it { expect(exercise.choice_index_for '1773').to eq 2 }
17
- end
18
-
19
- context 'when choices are in 6.0 format' do
20
- let(:choices) { [{value: '1492', checked: false}, {value: '1453', checked: true}, {value: '1773', checked: false}] }
21
- let(:exercise) { build(:exercise, description: 'when did byzantine empire fall?', choices: choices) }
22
-
23
- it { expect(exercise.choices).to eq choices }
24
- it { expect(exercise[:choice_values]).to be_blank }
25
- it { expect(exercise.choice_values).to eq %w(1492 1453 1773) }
26
- it { expect(exercise.choice_index_for '1492').to eq 0 }
27
- it { expect(exercise.choice_index_for '1773').to eq 2 }
28
- end
29
- end
30
-
31
- describe '#slug' do
32
- let(:guide) { create(:guide, slug: 'foo/bar') }
33
- let(:exercise) { create(:exercise, guide: guide, bibliotheca_id: 4) }
34
- it { expect(exercise.slug).to eq 'foo/bar/4' }
35
- end
36
-
37
- describe '#new_solution' do
38
- context 'when there is default content' do
39
- let(:exercise) { create(:exercise, default_content: 'foo') }
40
-
41
- it { expect(exercise.new_solution.content).to eq 'foo' }
42
- end
43
-
44
- context 'when there is no default content' do
45
- let(:exercise) { create(:exercise) }
46
-
47
- it { expect(exercise.new_solution.content).to be_blank }
48
- end
49
- end
50
-
51
- describe '#next_for' do
52
- context 'when exercise has no guide' do
53
- it { expect(exercise.next_for(user)).to be nil }
54
- end
55
- context 'when exercise belong to a guide with a single exercise' do
56
- let(:exercise_with_guide) { create(:exercise, guide: guide) }
57
- let(:guide) { create(:guide) }
58
-
59
- it { expect(exercise_with_guide.next_for(user)).to be nil }
60
- end
61
- context 'when exercise belongs to a guide with two exercises' do
62
- let!(:exercise_with_guide) { create(:exercise, guide: guide, number: 2) }
63
- let!(:alternative_exercise) { create(:exercise, guide: guide, number: 3) }
64
- let!(:guide) { create(:guide) }
65
-
66
- it { expect(exercise_with_guide.next_for(user)).to eq alternative_exercise }
67
- end
68
-
69
- context 'when exercise belongs to a guide with two exercises and alternative exercise has being solved' do
70
- let(:exercise_with_guide) { create(:exercise, guide: guide) }
71
- let!(:alternative_exercise) { create(:exercise, guide: guide) }
72
- let(:guide) { create(:guide) }
73
-
74
- before { alternative_exercise.submit_solution!(user, content: 'foo').passed! }
75
-
76
- it { expect(exercise_with_guide.next_for(user)).to be nil }
77
- end
78
-
79
- context 'when exercise belongs to a guide with two exercises and alternative exercise has being submitted but not solved' do
80
- let!(:exercise_with_guide) { create(:exercise, guide: guide, number: 2) }
81
- let!(:alternative_exercise) { create(:exercise, guide: guide, number: 3) }
82
- let(:guide) { create(:guide) }
83
-
84
- before { alternative_exercise.submit_solution!(user, content: 'foo') }
85
-
86
- it { expect(guide.pending_exercises(user)).to_not eq [] }
87
- it { expect(guide.next_exercise(user)).to_not be nil }
88
- it { expect(guide.pending_exercises(user)).to include(alternative_exercise) }
89
- it { expect(exercise_with_guide.next_for(user)).to eq alternative_exercise }
90
- it { expect(guide.exercises).to_not eq [] }
91
- it { expect(exercise_with_guide.guide).to eq guide }
92
- it { expect(guide.pending_exercises(user)).to include(exercise_with_guide) }
93
- end
94
- end
95
-
96
- describe '#extra' do
97
- context 'when exercise has no extra code' do
98
- it { expect(exercise.extra).to eq '' }
99
- end
100
-
101
- context 'when exercise has extra code and has no guide' do
102
- let!(:exercise_with_extra) { create(:exercise, extra: 'exercise extra code') }
103
-
104
- it { expect(exercise_with_extra.extra).to eq "exercise extra code\n" }
105
- end
106
-
107
- context 'when exercise has extra code and ends with new line and has no guide' do
108
- let!(:exercise_with_extra) { create(:exercise, extra: "exercise extra code\n") }
109
-
110
- it { expect(exercise_with_extra.extra).to eq "exercise extra code\n" }
111
- end
112
-
113
- context 'when exercise has extra code and belongs to a guide with no extra code' do
114
- let!(:exercise_with_extra) { create(:exercise, guide: guide, extra: 'exercise extra code') }
115
- let!(:guide) { create(:guide) }
116
-
117
- it { expect(exercise_with_extra.extra).to eq "exercise extra code\n" }
118
- end
119
-
120
- context 'when exercise has extra code with trailing whitespaces
121
- and belongs to a guide with no extra code' do
122
- let!(:exercise_with_extra) { create(:exercise, guide: guide, extra: "\nexercise extra code \n") }
123
- let!(:guide) { create(:guide) }
124
-
125
- it { expect(exercise_with_extra.extra).to eq "exercise extra code\n" }
126
- end
127
-
128
- context 'when exercise has extra code and belongs to a guide with extra code' do
129
- let!(:exercise_with_extra) { create(:exercise, guide: guide, extra: 'exercise extra code') }
130
- let!(:guide) { create(:guide, extra: 'guide extra code') }
131
-
132
- it { expect(exercise_with_extra.extra).to eq "guide extra code\nexercise extra code\n" }
133
- it { expect(exercise_with_extra[:extra]).to eq 'exercise extra code' }
134
- end
135
- end
136
-
137
- describe '#extra_preview' do
138
- let(:haskell) { create(:haskell) }
139
- let(:guide) { create(:guide,
140
- extra: 'f x = 1',
141
- language: haskell,
142
- exercises: [create(:exercise,
143
- extra: 'g y = y + 3',
144
- language: haskell)]) }
145
- let(:exercise) { guide.exercises.first }
146
-
147
- it { expect(exercise.assignment_for(user).extra_preview).to eq "```haskell\nf x = 1\ng y = y + 3\n```" }
148
- end
149
-
150
- describe '#submit_solution!' do
151
- context 'when user did a submission' do
152
- before { exercise.submit_solution!(user) }
153
- it { expect(exercise.find_assignment_for(user)).to be_present }
154
- end
155
- context 'when user did no submission' do
156
- it { expect(exercise.find_assignment_for(user)).to be_blank }
157
- end
158
- end
159
-
160
- describe '#destroy' do
161
- context 'when there are no submissions' do
162
- it { exercise.destroy! }
163
- end
164
-
165
- context 'when there are submissions' do
166
- let!(:assignment) { create(:assignment, exercise: exercise) }
167
- before { exercise.destroy! }
168
- it { expect { Assignment.find(assignment.id) }.to raise_error(ActiveRecord::RecordNotFound) }
169
- end
170
-
171
- end
172
-
173
- describe '#previous_solution_for' do
174
- context 'when user has a single submission for the exercise' do
175
- let!(:assignment) { exercise.submit_solution!(user, content: 'foo') }
176
-
177
- it { expect(assignment.current_content).to eq assignment.solution }
178
- end
179
-
180
- context 'when user has no submissions for the exercise' do
181
- it { expect(exercise.assignment_for(user).current_content).to eq '' }
182
- end
183
-
184
- context 'when using an interpolation' do
185
- let!(:chapter) {
186
- create(:chapter, lessons: [
187
- create(:lesson, exercises: [
188
- first_exercise,
189
- second_exercise,
190
- previous_exercise,
191
- exercise
192
- ])]) }
193
-
194
- let(:first_exercise) { create(:exercise) }
195
- let(:second_exercise) { create(:exercise) }
196
- let(:previous_exercise) { create(:exercise) }
197
-
198
- before { reindex_current_organization! }
199
-
200
- context 'when interpolation is in default_content' do
201
- let(:assignment) { exercise.assignment_for(user) }
202
-
203
- describe 'right previous content' do
204
- let(:exercise) { create(:exercise, default_content: interpolation) }
205
-
206
- context 'using previousContent' do
207
- let(:interpolation) { '/*...previousContent...*/' }
208
-
209
- context 'has previous submission' do
210
- before { previous_exercise.submit_solution!(user, content: 'foobar') }
211
- it { expect(assignment.current_content).to eq 'foobar' }
212
- end
213
-
214
- context 'does not have previous submission' do
215
- it { expect(assignment.current_content).to eq '' }
216
- end
217
- end
218
- context 'using previousSolution' do
219
- let(:interpolation) { '/*...previousSolution...*/' }
220
-
221
- context 'has previous submission' do
222
- before { previous_exercise.submit_solution!(user, content: 'foobar') }
223
- it { expect(assignment.current_content).to eq 'foobar' }
224
- end
225
-
226
- context 'does not have previous submission' do
227
- it { expect(assignment.current_content).to eq '' }
228
- end
229
- end
230
- end
231
-
232
- describe 'indexed previous content' do
233
- context '-2 index' do
234
- let(:exercise) { create(:exercise, default_content: '/*...solution[-2]...*/') }
235
-
236
- context 'has previous submission' do
237
- before { second_exercise.submit_solution!(user, content: 'foobar') }
238
- it { expect(assignment.current_content).to eq 'foobar' }
239
- end
240
- end
241
- context '-1 index' do
242
- let(:exercise) { create(:exercise, default_content: '/*...solution[-1]...*/') }
243
-
244
- context 'has previous submission' do
245
- before { previous_exercise.submit_solution!(user, content: 'foobar') }
246
- it { expect(assignment.current_content).to eq 'foobar' }
247
- end
248
-
249
- context 'does not have previous submission' do
250
- it { expect(assignment.current_content).to eq '' }
251
- end
252
- end
253
- context '1 index' do
254
- let(:exercise) { create(:exercise, default_content: '/*...solution[1]...*/') }
255
-
256
- context 'has previous submission' do
257
- before { first_exercise.submit_solution!(user, content: 'foobar') }
258
- it { expect(assignment.current_content).to eq 'foobar' }
259
- end
260
- end
261
- context '2 index' do
262
- let(:exercise) { create(:exercise, default_content: '/*...solution[2]...*/') }
263
-
264
- context 'has previous submission' do
265
- before { second_exercise.submit_solution!(user, content: 'foobar') }
266
- it { expect(assignment.current_content).to eq 'foobar' }
267
- end
268
- end
269
- context '3 index' do
270
- let(:exercise) { create(:exercise, default_content: '/*...solution[3]...*/') }
271
-
272
- context 'has previous submission' do
273
- before { previous_exercise.submit_solution!(user, content: 'foobar') }
274
- it { expect(assignment.current_content).to eq 'foobar' }
275
- end
276
- end
277
- end
278
- end
279
- context 'when interpolation is in test' do
280
- let(:assignment) { exercise.assignment_for(user) }
281
-
282
- context 'using user_first_name' do
283
- let(:exercise) { create(:exercise, test: "<div>Hola #{interpolation}</div>") }
284
- let(:interpolation) { '/*...user_first_name...*/' }
285
-
286
- it { expect(assignment.test).to eq "<div>Hola Orlo</div>" }
287
- end
288
-
289
- context 'and test is nil' do
290
- let(:exercise) { create(:exercise, test: nil, expectations: [{binding: "program", inspection: 'Uses:foo'}]) }
291
-
292
- it { expect(assignment.test).to eq nil }
293
- end
294
- end
295
- context 'when interpolation is in extra' do
296
- let(:assignment) { exercise.assignment_for(user) }
297
-
298
- describe 'right previous content' do
299
- let(:exercise) { create(:exercise, extra: interpolation) }
300
-
301
- context 'using previousContent' do
302
- let(:interpolation) { '/*...previousContent...*/' }
303
-
304
- context 'has previous submission' do
305
- before { previous_exercise.submit_solution!(user, content: 'foobar') }
306
- it { expect(assignment.extra).to eq "foobar\n" }
307
- end
308
-
309
- context 'does not have previous submission' do
310
- it { expect(assignment.extra).to eq "\n" }
311
- end
312
- end
313
- context 'using previousSolution' do
314
- let(:interpolation) { '/*...previousSolution...*/' }
315
-
316
- context 'has previous submission' do
317
- before { previous_exercise.submit_solution!(user, content: 'foobar') }
318
- it { expect(assignment.extra).to eq "foobar\n" }
319
- end
320
-
321
- context 'does not have previous submission' do
322
- it { expect(assignment.extra).to eq "\n" }
323
- end
324
- end
325
- end
326
-
327
- describe 'indexed previous content' do
328
- context '-2 index' do
329
- let(:exercise) { create(:exercise, extra: '/*...solution[-2]...*/') }
330
-
331
- context 'has previous submission' do
332
- before { second_exercise.submit_solution!(user, content: 'foobar') }
333
- it { expect(assignment.extra).to eq "foobar\n" }
334
- end
335
- end
336
- context '-1 index' do
337
- let(:exercise) { create(:exercise, extra: '/*...solution[-1]...*/') }
338
-
339
- context 'has previous submission' do
340
- before { previous_exercise.submit_solution!(user, content: 'foobar') }
341
- it { expect(assignment.extra).to eq "foobar\n" }
342
- end
343
-
344
- context 'does not have previous submission' do
345
- it { expect(assignment.extra).to eq "\n" }
346
- end
347
- end
348
- context '1 index' do
349
- let(:exercise) { create(:exercise, extra: '/*...solution[1]...*/') }
350
-
351
- context 'has previous submission' do
352
- before { first_exercise.submit_solution!(user, content: 'foobar') }
353
- it { expect(assignment.extra).to eq "foobar\n" }
354
- end
355
- end
356
- context '2 index' do
357
- let(:exercise) { create(:exercise, extra: '/*...solution[2]...*/') }
358
-
359
- context 'has previous submission' do
360
- before { second_exercise.submit_solution!(user, content: 'foobar') }
361
- it { expect(assignment.extra).to eq "foobar\n" }
362
- end
363
- end
364
- context '3 index' do
365
- let(:exercise) { create(:exercise, extra: '/*...solution[3]...*/') }
366
-
367
- context 'has previous submission' do
368
- before { previous_exercise.submit_solution!(user, content: 'foobar') }
369
- it { expect(assignment.extra).to eq "foobar\n" }
370
- end
371
- end
372
- end
373
- end
374
- end
375
-
376
- context 'when user has multiple submission for the exercise' do
377
- before { exercise.submit_solution!(user, content: 'foo') }
378
- let!(:assignment) { exercise.submit_solution!(user, content: 'bar') }
379
-
380
- it { expect(assignment.current_content).to eq assignment.solution }
381
- end
382
-
383
- context 'when user has no solution and exercise has default content' do
384
- let(:exercise) { create(:exercise, default_content: '#write here...') }
385
- let(:assignment) { exercise.assignment_for user }
386
-
387
- it { expect(assignment.current_content).to eq '#write here...' }
388
- end
389
- end
390
-
391
- describe '#guide_done_for?' do
392
-
393
- context 'when it does not belong to a guide' do
394
- it { expect(exercise.guide_done_for?(user)).to be false }
395
- end
396
-
397
- context 'when it belongs to a guide unfinished' do
398
- let!(:guide) { create(:guide) }
399
- let!(:exercise_unfinished) { create(:exercise, guide: guide) }
400
- let!(:exercise_finished) { create(:exercise, guide: guide) }
401
-
402
- before do
403
- exercise_finished.submit_solution!(user, content: 'foo').passed!
404
- end
405
-
406
- it { expect(exercise_finished.guide_done_for?(user)).to be false }
407
- end
408
-
409
- context 'when it belongs to a guide unfinished' do
410
- let!(:guide) { create(:guide) }
411
- let!(:exercise_finished) { create(:exercise, guide: guide) }
412
- let!(:exercise_finished2) { create(:exercise, guide: guide) }
413
-
414
- before do
415
- exercise_finished.submit_solution!(user, content: 'foo').passed!
416
- exercise_finished2.submit_solution!(user, content: 'foo').passed!
417
- end
418
-
419
- it { expect(exercise_finished.guide_done_for?(user)).to be true }
420
- end
421
- end
422
-
423
- describe '#language' do
424
- let(:guide) { create(:guide) }
425
- let(:exercise_with_guide) { create(:exercise, guide: guide, language: guide.language) }
426
- let(:other_language) { create(:language) }
427
-
428
- context 'when has no guide' do
429
- it { expect(exercise.valid?).to be true }
430
- end
431
-
432
- context 'when has guide and is consistent' do
433
- it { expect(exercise_with_guide.valid?).to be true }
434
- end
435
- end
436
-
437
- describe '#friendly_name' do
438
- it { expect(Exercise.find(exercise.friendly_name)).to eq exercise }
439
- it { expect(Problem.find(exercise.friendly_name)).to eq exercise }
440
- end
441
-
442
- describe 'messages_path_for' do
443
- let(:haskell) { create(:haskell) }
444
- let(:problem) { create(:problem, bibliotheca_id: 32, guide: guide, language: haskell) }
445
- let(:guide) { create(:guide, slug: 'mumuki/myguide') }
446
-
447
- context 'user with email uid' do
448
- let(:student) { create(:user, uid: 'foo@bar.com') }
449
-
450
- it { expect(problem.messages_path_for(student))
451
- .to eq 'api/guides/mumuki/myguide/32/student/foo@bar.com/messages?language=haskell' }
452
- it { expect(problem.messages_url_for(student))
453
- .to eq 'http://test.classroom-api.localmumuki.io/api/guides/mumuki/myguide/32/student/foo@bar.com/messages?language=haskell' }
454
- end
455
-
456
- context 'user with twitter uid' do
457
- let(:student) { create(:user, uid: 'twitter|12134342') }
458
-
459
- it { expect(problem.messages_url_for(student))
460
- .to eq 'http://test.classroom-api.localmumuki.io/api/guides/mumuki/myguide/32/student/twitter%7C12134342/messages?language=haskell' }
461
- end
462
- end
463
-
464
- describe '#splitted_description' do
465
- let(:exercise) { create(:exercise, description: "**Foo**\n\n> _Bar_") }
466
- it { expect(exercise.description_context).to eq "<p><strong>Foo</strong></p>\n" }
467
- it { expect(exercise.description_task).to eq "<p><em>Bar</em></p>\n" }
468
- end
469
-
470
- describe '#validate!' do
471
- context 'non-empty, valid randomizations' do
472
- let(:exercise) { build(:exercise,
473
- randomizations: {
474
- some_word: { type: :one_of, value: %w('some' 'word') },
475
- some_number: { type: :range, value: [1, 10] } }) }
476
- it { expect { exercise.validate! }.not_to raise_error }
477
- end
478
-
479
- context 'empty inspections' do
480
- let(:exercise) { build(:exercise, expectations: [{ "binding" => "program", "inspection" => "" }]) }
481
- it { expect { exercise.validate! }.to raise_error(/expectations format is invalid/i) }
482
- end
483
-
484
- context 'invalid assistance_rules' do
485
- let(:exercise) { build(:exercise, assistance_rules: [{ when: 'content_empty', then: ['asd'] }]) }
486
- it { expect { exercise.validate! }.to raise_error(/assistance rules format is invalid/i) }
487
- end
488
-
489
- context 'invalid randomizations' do
490
- let(:exercise) { build(:exercise, randomizations: { type: :range, value: [1] }) }
491
- it { expect { exercise.validate! }.to raise_error(/randomizations format is invalid/i) }
492
- end
493
- end
494
- end