mumuki-laboratory 9.19.0 → 9.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/mumuki_laboratory/application/_layout.scss +0 -8
  3. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +1 -3
  4. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_user_profile.scss +1 -9
  5. data/app/assets/stylesheets/mumuki_laboratory/application.scss +0 -1
  6. data/app/controllers/appendixes_controller.rb +7 -0
  7. data/app/controllers/application_controller.rb +6 -2
  8. data/app/controllers/complements_controller.rb +13 -0
  9. data/app/helpers/application_helper.rb +3 -1
  10. data/app/helpers/discussions_helper.rb +41 -26
  11. data/app/helpers/profile_helper.rb +1 -1
  12. data/app/views/book/show.html.erb +21 -18
  13. data/app/views/chapters/show.html.erb +1 -1
  14. data/app/views/discussions/_description_message.html.erb +6 -4
  15. data/app/views/discussions/_message.html.erb +10 -8
  16. data/app/views/discussions/show.html.erb +1 -1
  17. data/app/views/errors/forbidden.html.erb +1 -3
  18. data/app/views/errors/gone.html.erb +1 -2
  19. data/app/views/errors/not_found.html.erb +1 -1
  20. data/app/views/guides/_guide.html.erb +9 -5
  21. data/app/views/layouts/_discussions.html.erb +1 -3
  22. data/app/views/users/_basic_profile_fields.html.erb +20 -16
  23. data/app/views/users/_edit_user_form.html.erb +9 -7
  24. data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +6 -6
  25. data/lib/mumuki/laboratory/controllers/validate_access_mode.rb +5 -1
  26. data/lib/mumuki/laboratory/locales/es-CL.yml +1 -1
  27. data/lib/mumuki/laboratory/locales/es.yml +1 -1
  28. data/lib/mumuki/laboratory/version.rb +1 -1
  29. data/spec/controllers/discussions_messages_controller_spec.rb +5 -5
  30. data/spec/controllers/messages_controller_spec.rb +3 -3
  31. data/spec/dummy/db/schema.rb +4 -1
  32. data/spec/features/discussion_flow_spec.rb +2 -2
  33. data/spec/features/guides_flow_spec.rb +29 -7
  34. data/spec/features/immersive_redirection_spec.rb +1 -1
  35. data/spec/features/not_found_public_flow_spec.rb +8 -1
  36. data/spec/features/profile_flow_spec.rb +3 -1
  37. data/spec/features/read_only_flow_spec.rb +72 -1
  38. metadata +107 -108
  39. data/app/assets/stylesheets/mumuki_laboratory/application/_alerts.scss +0 -3
@@ -6,7 +6,7 @@ module Mumuki::Laboratory::Controllers::ValidateAccessMode
6
6
  end
7
7
 
8
8
  def validate_accessible!
9
- current_access_mode.validate_content_here! subject_container
9
+ current_access_mode.validate_content_here! subject_container unless contentless_subject?
10
10
  super
11
11
  end
12
12
 
@@ -14,6 +14,10 @@ module Mumuki::Laboratory::Controllers::ValidateAccessMode
14
14
  subject
15
15
  end
16
16
 
17
+ def contentless_subject?
18
+ false
19
+ end
20
+
17
21
  def accessible_subject
18
22
  nil
19
23
  end
@@ -291,7 +291,7 @@ es-CL:
291
291
  one: '1 abierta'
292
292
  other: '%{count} abiertas'
293
293
  organizations: Organizaciones
294
- organization_read_only_legend: 'Estás en modo lectura. Solo podrás acceder a los ejercicios que realizaste previamente y no podrás enviar nuevas soluciones'
294
+ organization_read_only_legend: 'Estás en Modo Lectura: como el curso ya finalizó no podrás enviar soluciones'
295
295
  other: Otro
296
296
  out_of_attempts: Te quedaste sin intentos. ¡Sigue al próximo ejercicio!
297
297
  output: Salida
@@ -300,7 +300,7 @@ es:
300
300
  one: '1 abierta'
301
301
  other: '%{count} abiertas'
302
302
  organizations: Organizaciones
303
- organization_read_only_legend: 'Estás en modo lectura. Solo podrás acceder a los ejercicios que realizaste previamente y no podrás enviar nuevas soluciones'
303
+ organization_read_only_legend: 'Estás en Modo Lectura: como el curso ya finalizó no podrás enviar soluciones'
304
304
  other: Otro
305
305
  out_of_attempts: Te quedaste sin intentos. ¡Seguí al proximo ejercicio!
306
306
  output: Salida
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Laboratory
3
- VERSION = '9.19.0'
3
+ VERSION = '9.22.0'
4
4
  end
5
5
  end
@@ -32,7 +32,7 @@ describe DiscussionsMessagesController, type: :controller, organization_workspac
32
32
  end
33
33
 
34
34
  describe 'delete' do
35
- let(:message) { create(:message, discussion: discussion, sender: student.uid) }
35
+ let(:message) { create(:message, discussion: discussion, sender: student) }
36
36
 
37
37
  describe 'for student' do
38
38
  before { set_current_user! student }
@@ -64,7 +64,7 @@ describe DiscussionsMessagesController, type: :controller, organization_workspac
64
64
  end
65
65
 
66
66
  describe 'someone else\'s message' do
67
- let(:message) { create(:message, discussion: discussion, sender: moderator.uid) }
67
+ let(:message) { create(:message, discussion: discussion, sender: moderator) }
68
68
  before do
69
69
  delete :destroy, params: {id: message.id, discussion_id: discussion.id, motive: :self_deleted}
70
70
  message.reload
@@ -94,7 +94,7 @@ describe DiscussionsMessagesController, type: :controller, organization_workspac
94
94
  end
95
95
 
96
96
  describe 'approve' do
97
- let(:message) { create(:message, discussion: discussion, sender: student.uid) }
97
+ let(:message) { create(:message, discussion: discussion, sender: student) }
98
98
 
99
99
  describe 'for student' do
100
100
  before { set_current_user! student }
@@ -114,7 +114,7 @@ describe DiscussionsMessagesController, type: :controller, organization_workspac
114
114
  end
115
115
 
116
116
  describe 'question' do
117
- let(:message) { create(:message, discussion: discussion, sender: student.uid) }
117
+ let(:message) { create(:message, discussion: discussion, sender: student) }
118
118
 
119
119
  describe 'for student' do
120
120
  before { set_current_user! student }
@@ -133,7 +133,7 @@ describe DiscussionsMessagesController, type: :controller, organization_workspac
133
133
  end
134
134
 
135
135
  describe 'preview' do
136
- let(:message) { create(:message, content: 'Message in **bold** and _italics_', discussion: discussion, sender: student.uid) }
136
+ let(:message) { create(:message, content: 'Message in **bold** and _italics_', discussion: discussion, sender: student) }
137
137
 
138
138
  describe 'for student' do
139
139
  before { set_current_user! student }
@@ -11,10 +11,10 @@ describe MessagesController, organization_workspace: :test do
11
11
 
12
12
  it { expect(response.status).to eq 302 }
13
13
  it { expect(user.assignments.size).to eq 1 }
14
- it { expect(user.messages.size).to eq 1 }
14
+ it { expect(user.direct_messages.size).to eq 1 }
15
15
 
16
16
  describe 'deleting exercises does delete all messages' do
17
- before { @message_id = user.messages.first.id }
17
+ before { @message_id = user.direct_messages.first.id }
18
18
  before { exercise.destroy }
19
19
 
20
20
  it { expect { Message.find(@message_id) }.to raise_exception(ActiveRecord::RecordNotFound) }
@@ -27,6 +27,6 @@ describe MessagesController, organization_workspace: :test do
27
27
 
28
28
  it { expect(response.status).to eq 302 }
29
29
  it { expect(user.assignments.size).to eq 1 }
30
- it { expect(user.messages.size).to eq 1 }
30
+ it { expect(user.direct_messages.size).to eq 1 }
31
31
  end
32
32
  end
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20210929223144) do
13
+ ActiveRecord::Schema.define(version: 20211020224011) do
14
14
 
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "plpgsql"
@@ -387,9 +387,12 @@ ActiveRecord::Schema.define(version: 20210929223144) do
387
387
  t.datetime "deleted_at"
388
388
  t.bigint "deleted_by_id"
389
389
  t.bigint "assignment_id"
390
+ t.bigint "sender_id"
391
+ t.boolean "from_moderator"
390
392
  t.index ["approved_by_id"], name: "index_messages_on_approved_by_id"
391
393
  t.index ["assignment_id"], name: "index_messages_on_assignment_id"
392
394
  t.index ["deleted_by_id"], name: "index_messages_on_deleted_by_id"
395
+ t.index ["sender_id"], name: "index_messages_on_sender_id"
393
396
  end
394
397
 
395
398
  create_table "notifications", force: :cascade do |t|
@@ -162,8 +162,8 @@ feature 'Discussion Flow', organization_workspace: :test do
162
162
  end
163
163
 
164
164
  context 'and forum enabled' do
165
- let!(:problem_2_discussion_message) { create(:message, discussion: problem_2_discussions.first, sender: another_student.uid) }
166
- let!(:another_problem_2_discussion_message) { create(:message, discussion: problem_2_discussions.first, sender: another_student.uid) }
165
+ let!(:problem_2_discussion_message) { create(:message, discussion: problem_2_discussions.first, sender: another_student) }
166
+ let!(:another_problem_2_discussion_message) { create(:message, discussion: problem_2_discussions.first, sender: another_student) }
167
167
  before do
168
168
  Organization.current.update! forum_enabled: true
169
169
  another_problem_2_discussion_message.soft_delete! :inappropriate_content, moderator
@@ -2,19 +2,28 @@ require 'spec_helper'
2
2
 
3
3
  feature 'Guides Flow', organization_workspace: :test do
4
4
  let(:haskell) { create(:haskell) }
5
- let!(:exercises) { [
6
- create(:exercise, name: 'Foo', guide: guide, number: 1, description: 'Description of foo'),
7
- create(:exercise, name: 'Bar', guide: guide, number: 2),
8
- create(:exercise, name: 'Baz', guide: guide, number: 4)
9
- ] }
10
- let(:guide) { create(:guide, name: 'awesomeGuide', description: 'An awesome guide', language: haskell, slug: 'foo/bar', authors: authors) }
5
+ let(:exercises) do
6
+ [
7
+ create(:exercise, name: 'Foo', number: 1, description: 'Description of foo'),
8
+ create(:exercise, name: 'Bar', number: 2),
9
+ create(:exercise, name: 'Baz', number: 4)
10
+ ]
11
+ end
12
+ let(:guide) do
13
+ create(:guide,
14
+ name: 'awesomeGuide',
15
+ description: 'An awesome guide',
16
+ language: haskell,
17
+ slug: 'foo/bar',
18
+ authors: authors,
19
+ exercises: exercises)
20
+ end
11
21
  let(:authors) { nil }
12
22
  let(:guide_not_in_path) { create(:guide) }
13
23
 
14
24
  let!(:lesson) { create(:lesson, guide: guide) }
15
25
  let!(:chapter) { create(:chapter, name: 'C1', lessons: [lesson]) }
16
26
 
17
-
18
27
  let!(:complement) { create(:complement, name: 'a complement', exercises: [
19
28
  create(:exercise, name: 'complementary exercise 1'),
20
29
  create(:exercise, name: 'complementary exercise 2')
@@ -48,6 +57,19 @@ feature 'Guides Flow', organization_workspace: :test do
48
57
  end
49
58
 
50
59
  context 'existent guide' do
60
+
61
+ context 'no exercises' do
62
+ let(:exercises) { []}
63
+
64
+ scenario 'visit lesson by id' do
65
+ visit "/guides/#{lesson.guide.id}"
66
+
67
+ expect(page).to have_text('awesomeGuide')
68
+ expect(page).to have_text('An awesome guide')
69
+ expect(page).to_not have_text('Exercises')
70
+ end
71
+ end
72
+
51
73
  context 'no authors' do
52
74
  let(:authors) { '' }
53
75
 
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  feature 'Immersive redirection Flow', organization_workspace: :test, subdomain_redirection_without_port: true do
4
4
  def create_guide(name)
5
- create(:guide, name: name)
5
+ create(:guide, name: name, exercises: [create(:exercise)])
6
6
  end
7
7
 
8
8
  def create_immersive_organization(name, guides)
@@ -6,7 +6,14 @@ feature 'not found public on app' do
6
6
  let!(:some_orga) { create(:public_organization, name: 'someorga', profile: profile) }
7
7
 
8
8
  let(:profile) { Mumuki::Domain::Organization::Profile.parse json }
9
- let(:json) { { contact_email: 'some@email.com', locale: 'en', time_zone: 'Brasilia', errors_explanations: { 404 => 'Some explanation'} } }
9
+ let(:json) do
10
+ {
11
+ contact_email: 'some@email.com',
12
+ locale: 'en',
13
+ time_zone: 'Brasilia',
14
+ errors_explanations: { "not_found" => 'Some explanation'}
15
+ }
16
+ end
10
17
 
11
18
  scenario 'when route does not exist in explicit central' do
12
19
  set_subdomain_host! 'test'
@@ -14,7 +14,7 @@ feature 'Profile Flow', organization_workspace: :test do
14
14
  'submission_id' => problem.assignments.last.submission_id,
15
15
  'organization' => 'test-organization',
16
16
  'message' => {
17
- 'sender' => 'test-email@gmail.com',
17
+ 'sender' => create(:user).uid,
18
18
  'content' => 'a',
19
19
  'created_at' => '1/1/1'}} }
20
20
  let(:organization) { create(:organization, name: 'test-organization') }
@@ -104,6 +104,8 @@ feature 'Profile Flow', organization_workspace: :test do
104
104
  end
105
105
 
106
106
  context 'with messages' do
107
+ before { create :user, uid: 'test-email@gmail.com' }
108
+
107
109
  scenario 'visit messages' do
108
110
  Organization.find_by_name('test').switch!
109
111
  problem.submit_solution! user, {content: 'something'}
@@ -14,14 +14,18 @@ feature 'Read Only Flow' do
14
14
  let(:exercise122) { create :exercise, name: 'Exercise 122' }
15
15
  let(:exercise211) { create :exercise, name: 'Exercise 211' }
16
16
  let(:exercise212) { create :exercise, name: 'Exercise 212' }
17
+ let(:exercise31) { create :exercise, name: 'Exercise 31' }
18
+ let(:exercise32) { create :exercise, name: 'Exercise 32' }
17
19
  let(:lesson11) { create :lesson, name: 'Lesson 11', exercises: [exercise111, exercise112] }
18
20
  let(:lesson12) { create :lesson, name: 'Lesson 12', exercises: [exercise121, exercise122] }
19
21
  let(:lesson21) { create :lesson, name: 'Lesson 21', exercises: [exercise211, exercise212] }
22
+ let(:complement3) { build :complement, name: 'Complement 3', exercises: [exercise31, exercise32] }
20
23
  let(:chapter1) { create :chapter, name: 'Chapter 1', lessons: [lesson11, lesson12] }
21
24
  let(:chapter2) { create :chapter, name: 'Chapter 2', lessons: [lesson21] }
22
- let(:book) { create :book, chapters: [chapter1, chapter2] }
25
+ let(:book) { create :book, chapters: [chapter1, chapter2], complements: [complement3] }
23
26
 
24
27
  let(:assignment111) { build :assignment, submitter: user, organization: organization, exercise: exercise111, status: :failed }
28
+ let(:assignment31) { build :assignment, submitter: user, organization: organization, exercise: exercise31, status: :failed }
25
29
  let(:discussion111) { build :discussion, initiator: assignment111.user, item: assignment111.exercise, organization: assignment111.organization }
26
30
 
27
31
  let(:assignment112) { build :assignment, submitter: other_user, organization: organization, exercise: exercise112, status: :failed }
@@ -32,6 +36,7 @@ feature 'Read Only Flow' do
32
36
  before { set_current_user! user }
33
37
 
34
38
  before { assignment111.save! }
39
+ before { assignment31.save! }
35
40
  before { discussion111.save! }
36
41
  before { discussion112.save! }
37
42
 
@@ -72,6 +77,12 @@ feature 'Read Only Flow' do
72
77
  expect(page).to have_text('Exercise 112')
73
78
  expect(page).to have_text('Continue this lesson!')
74
79
  end
80
+ scenario 'show complement' do
81
+ visit "/complements/#{complement3.id}"
82
+ expect(page).to have_text('Complement 3')
83
+ expect(page).to have_text('Exercise 31')
84
+ expect(page).to have_text('Exercise 32')
85
+ end
75
86
  scenario 'show exercise 111' do
76
87
  visit "/exercises/#{exercise111.id}"
77
88
  expect(page).to have_text('Exercise 111')
@@ -146,6 +157,12 @@ feature 'Read Only Flow' do
146
157
  expect(page).to have_text('Exercise 112')
147
158
  expect(page).to have_text('Continue this lesson!')
148
159
  end
160
+ scenario 'show complement' do
161
+ visit "/complements/#{complement3.id}"
162
+ expect(page).to have_text('Complement 3')
163
+ expect(page).to have_text('Exercise 31')
164
+ expect(page).to have_text('Exercise 32')
165
+ end
149
166
  scenario 'show exercise 111' do
150
167
  visit "/exercises/#{exercise111.id}"
151
168
  expect(page).to have_text('Exercise 111')
@@ -222,6 +239,12 @@ feature 'Read Only Flow' do
222
239
  expect(page).not_to have_text('Exercise 112')
223
240
  expect(page).not_to have_text('Continue this lesson!')
224
241
  end
242
+ scenario 'show complement' do
243
+ visit "/complements/#{complement3.id}"
244
+ expect(page).to have_text('Complement 3')
245
+ expect(page).to have_text('Exercise 31')
246
+ expect(page).to_not have_text('Exercise 32')
247
+ end
225
248
  scenario 'show exercise 111' do
226
249
  visit "/exercises/#{exercise111.id}"
227
250
  expect(page).to have_text('Exercise 111')
@@ -297,6 +320,10 @@ feature 'Read Only Flow' do
297
320
  visit "/lessons/#{lesson11.id}"
298
321
  expect(page).to have_text('You are not allowed to see this content')
299
322
  end
323
+ scenario 'show complement' do
324
+ visit "/complements/#{complement3.id}"
325
+ expect(page).to have_text('You are not allowed to see this content')
326
+ end
300
327
  scenario 'show exercise 111' do
301
328
  visit "/exercises/#{exercise111.id}"
302
329
  expect(page).to have_text('You are not allowed to see this content')
@@ -370,6 +397,12 @@ feature 'Read Only Flow' do
370
397
  expect(page).to have_text('Exercise 112')
371
398
  expect(page).to have_text('Continue this lesson!')
372
399
  end
400
+ scenario 'show complement' do
401
+ visit "/complements/#{complement3.id}"
402
+ expect(page).to have_text('Complement 3')
403
+ expect(page).to have_text('Exercise 31')
404
+ expect(page).to have_text('Exercise 32')
405
+ end
373
406
  scenario 'show exercise 111' do
374
407
  visit "/exercises/#{exercise111.id}"
375
408
  expect(page).to have_text('Exercise 111')
@@ -434,6 +467,10 @@ feature 'Read Only Flow' do
434
467
  visit "/lessons/#{lesson11.id}"
435
468
  expect(page).to have_text('This path hasn\'t started yet')
436
469
  end
470
+ scenario 'show complemen' do
471
+ visit "/complemens/#{complement3.id}"
472
+ expect(page).to have_text('This path hasn\'t started yet')
473
+ end
437
474
  scenario 'show exercise 111' do
438
475
  visit "/exercises/#{exercise111.id}"
439
476
  expect(page).to have_text('This path hasn\'t started yet')
@@ -495,6 +532,10 @@ feature 'Read Only Flow' do
495
532
  visit "/lessons/#{lesson11.id}"
496
533
  expect(page).to have_text('You are not allowed to see this content')
497
534
  end
535
+ scenario 'show complement' do
536
+ visit "/complements/#{complement3.id}"
537
+ expect(page).to have_text('You are not allowed to see this content')
538
+ end
498
539
  scenario 'show exercise 111' do
499
540
  visit "/exercises/#{exercise111.id}"
500
541
  expect(page).to have_text('You are not allowed to see this content')
@@ -550,6 +591,10 @@ feature 'Read Only Flow' do
550
591
  visit "/lessons/#{lesson11.id}"
551
592
  expect(page).to have_text('You are not allowed to see this content')
552
593
  end
594
+ scenario 'show complement' do
595
+ visit "/complements/#{complement3.id}"
596
+ expect(page).to have_text('You are not allowed to see this content')
597
+ end
553
598
  scenario 'show exercise 111' do
554
599
  visit "/exercises/#{exercise111.id}"
555
600
  expect(page).to have_text('You are not allowed to see this content')
@@ -623,6 +668,12 @@ feature 'Read Only Flow' do
623
668
  expect(page).to have_text('Exercise 112')
624
669
  expect(page).to have_text('Continue this lesson!')
625
670
  end
671
+ scenario 'show complement' do
672
+ visit "/complements/#{complement3.id}"
673
+ expect(page).to have_text('Complement 3')
674
+ expect(page).to have_text('Exercise 31')
675
+ expect(page).to have_text('Exercise 32')
676
+ end
626
677
  scenario 'show exercise 111' do
627
678
  visit "/exercises/#{exercise111.id}"
628
679
  expect(page).to have_text('Exercise 111')
@@ -697,6 +748,12 @@ feature 'Read Only Flow' do
697
748
  expect(page).to have_text('Exercise 112')
698
749
  expect(page).not_to have_text('Continue this lesson!')
699
750
  end
751
+ scenario 'show complement' do
752
+ visit "/complements/#{complement3.id}"
753
+ expect(page).to have_text('Complement 3')
754
+ expect(page).to have_text('Exercise 31')
755
+ expect(page).to have_text('Exercise 32')
756
+ end
700
757
  scenario 'show exercise 111' do
701
758
  visit "/exercises/#{exercise111.id}"
702
759
  expect(page).to have_text('Exercise 111')
@@ -763,6 +820,10 @@ feature 'Read Only Flow' do
763
820
  visit "/lessons/#{lesson11.id}"
764
821
  expect(page).to have_text('You are not allowed to see this content')
765
822
  end
823
+ scenario 'show complement' do
824
+ visit "/complements/#{complement3.id}"
825
+ expect(page).to have_text('You are not allowed to see this content')
826
+ end
766
827
  scenario 'show exercise 111' do
767
828
  visit "/exercises/#{exercise111.id}"
768
829
  expect(page).to have_text('You are not allowed to see this content')
@@ -819,6 +880,10 @@ feature 'Read Only Flow' do
819
880
  visit "/lessons/#{lesson11.id}"
820
881
  expect(page).to have_text('You are not allowed to see this content')
821
882
  end
883
+ scenario 'show complement' do
884
+ visit "/complements/#{complement3.id}"
885
+ expect(page).to have_text('You are not allowed to see this content')
886
+ end
822
887
  scenario 'show exercise 111' do
823
888
  visit "/exercises/#{exercise111.id}"
824
889
  expect(page).to have_text('You are not allowed to see this content')
@@ -889,6 +954,12 @@ feature 'Read Only Flow' do
889
954
  expect(page).to have_text('Exercise 112')
890
955
  expect(page).to have_text('Continue this lesson!')
891
956
  end
957
+ scenario 'show complement' do
958
+ visit "/complements/#{complement3.id}"
959
+ expect(page).to have_text('Complement 3')
960
+ expect(page).to have_text('Exercise 31')
961
+ expect(page).to have_text('Exercise 32')
962
+ end
892
963
  scenario 'show exercise 111' do
893
964
  visit "/exercises/#{exercise111.id}"
894
965
  expect(page).to have_text('Exercise 111')