mumuki-laboratory 9.20.0 → 9.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -0
  3. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +1 -3
  4. data/app/controllers/appendixes_controller.rb +7 -0
  5. data/app/controllers/application_controller.rb +6 -3
  6. data/app/controllers/complements_controller.rb +13 -0
  7. data/app/controllers/login_controller.rb +11 -3
  8. data/app/controllers/users_controller.rb +11 -4
  9. data/app/helpers/application_helper.rb +3 -1
  10. data/app/helpers/discussions_helper.rb +41 -26
  11. data/app/helpers/mailer_helper.rb +5 -0
  12. data/app/helpers/user_menu_helper.rb +7 -3
  13. data/app/mailers/application_mailer.rb +1 -0
  14. data/app/mailers/user_mailer.rb +6 -1
  15. data/app/views/book/show.html.erb +21 -18
  16. data/app/views/chapters/show.html.erb +1 -1
  17. data/app/views/discussions/_description_message.html.erb +6 -4
  18. data/app/views/discussions/_message.html.erb +10 -8
  19. data/app/views/discussions/show.html.erb +1 -1
  20. data/app/views/errors/forbidden.html.erb +1 -3
  21. data/app/views/errors/gone.html.erb +1 -2
  22. data/app/views/errors/not_found.html.erb +1 -1
  23. data/app/views/guides/_guide.html.erb +9 -5
  24. data/app/views/layouts/_discussions.html.erb +1 -3
  25. data/app/views/layouts/_organization_chooser.html.erb +2 -2
  26. data/app/views/layouts/_user_menu.html.erb +2 -0
  27. data/app/views/user_mailer/delete_account.html.erb +7 -0
  28. data/app/views/users/_user_delete_confirmation.erb +17 -0
  29. data/app/views/users/_user_delete_modal.html.erb +35 -0
  30. data/app/views/users/delete_account.html.erb +27 -0
  31. data/app/views/users/delete_confirmation_invalid.html.erb +22 -0
  32. data/app/views/users/delete_request.html.erb +1 -0
  33. data/config/routes.rb +6 -3
  34. data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +6 -6
  35. data/lib/mumuki/laboratory/controllers/validate_access_mode.rb +5 -1
  36. data/lib/mumuki/laboratory/locales/en.yml +19 -2
  37. data/lib/mumuki/laboratory/locales/es-CL.yml +20 -3
  38. data/lib/mumuki/laboratory/locales/es.yml +21 -3
  39. data/lib/mumuki/laboratory/locales/pt.yml +20 -4
  40. data/lib/mumuki/laboratory/version.rb +1 -1
  41. data/spec/capybara_helper.rb +10 -10
  42. data/spec/controllers/discussions_messages_controller_spec.rb +5 -5
  43. data/spec/controllers/messages_controller_spec.rb +3 -3
  44. data/spec/controllers/users_controller_spec.rb +20 -1
  45. data/spec/dummy/config/environments/development.rb +8 -2
  46. data/spec/dummy/config/environments/test.rb +4 -1
  47. data/spec/dummy/db/schema.rb +5 -2
  48. data/spec/features/discussion_flow_spec.rb +2 -2
  49. data/spec/features/dynamic_exam_spec.rb +4 -2
  50. data/spec/features/guides_flow_spec.rb +29 -7
  51. data/spec/features/immersive_redirection_spec.rb +2 -2
  52. data/spec/features/not_found_public_flow_spec.rb +8 -1
  53. data/spec/features/profile_flow_spec.rb +3 -1
  54. data/spec/features/read_only_flow_spec.rb +72 -1
  55. data/spec/mailers/previews/user_mailer_preview.rb +4 -0
  56. data/spec/mailers/user_mailer_spec.rb +10 -3
  57. metadata +133 -108
  58. data/spec/features/disable_user_flow_spec.rb +0 -32
@@ -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"
@@ -373,7 +373,6 @@ ActiveRecord::Schema.define(version: 20210929223144) do
373
373
  create_table "messages", id: :serial, force: :cascade do |t|
374
374
  t.string "submission_id"
375
375
  t.text "content"
376
- t.string "sender"
377
376
  t.datetime "date"
378
377
  t.datetime "created_at"
379
378
  t.datetime "updated_at"
@@ -387,9 +386,13 @@ ActiveRecord::Schema.define(version: 20210929223144) do
387
386
  t.datetime "deleted_at"
388
387
  t.bigint "deleted_by_id"
389
388
  t.bigint "assignment_id"
389
+ t.bigint "sender_id"
390
+ t.boolean "from_moderator"
391
+ t.bigint "sender_id"
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
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  feature 'Dynamic Exam', organization_workspace: :test do
4
- let(:user) { create(:user, id: 1) }
4
+ let(:user) { create(:user, id: 5) }
5
5
  let(:user2) { create(:user, id: 2) }
6
6
 
7
- let!(:problem) { build(:problem, description: 'do f = $someVariable', randomizations: { someVariable: { type: :one_of, value: %w(some_string some_other_string)} }) }
7
+ let!(:problem) { build(:problem, description: 'do f = $someVariable', randomizations: { someVariable: { type: :one_of, value: %w(some_string some_other_string) } }) }
8
8
 
9
9
  let!(:chapter) {
10
10
  create(:chapter, lessons: [
@@ -30,7 +30,9 @@ feature 'Dynamic Exam', organization_workspace: :test do
30
30
  visit "/exercises/#{problem.transparent_id}"
31
31
 
32
32
  expect(page).to have_text('do f = some_other_string')
33
+ end
33
34
 
35
+ scenario 'visit exercise by transparent_id' do
34
36
  set_current_user! user2
35
37
  visit "/exercises/#{problem.transparent_id}"
36
38
 
@@ -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)
@@ -113,7 +113,7 @@ feature 'Immersive redirection Flow', organization_workspace: :test, subdomain_r
113
113
  end
114
114
 
115
115
  def choose_organization(name)
116
- within '.modal' do
116
+ within '#organization-chooser-modal' do
117
117
  click_on "Go to #{name}"
118
118
  end
119
119
  end
@@ -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')
@@ -40,6 +40,10 @@ class UserMailerPreview < ActionMailer::Preview
40
40
  UserMailer.notification notification
41
41
  end
42
42
 
43
+ def delete_account
44
+ UserMailer.delete_account user
45
+ end
46
+
43
47
  private
44
48
 
45
49
  def exam_registration
@@ -68,7 +68,7 @@ RSpec.describe UserMailer, type: :mailer do
68
68
  it "renders the headers" do
69
69
  expect(reminder.subject).to eq("We miss you!")
70
70
  expect(reminder.to).to eq([user.email])
71
- expect(reminder.from).to eq(["support@mumuki.org"])
71
+ expect(reminder.from).to eq(["no-reply@mumuki.org"])
72
72
  end
73
73
 
74
74
  context 'last reminded over 1 week ago' do
@@ -134,7 +134,7 @@ RSpec.describe UserMailer, type: :mailer do
134
134
  it "renders the headers" do
135
135
  expect(reminder.subject).to eq("Start using Mumuki!")
136
136
  expect(reminder.to).to eq([user.email])
137
- expect(reminder.from).to eq(["support@mumuki.org"])
137
+ expect(reminder.from).to eq(["no-reply@mumuki.org"])
138
138
  end
139
139
 
140
140
  context 'last reminded over 1 week ago' do
@@ -189,7 +189,7 @@ RSpec.describe UserMailer, type: :mailer do
189
189
  it { expect(email.body.encoded).to eq 'hello some name!' }
190
190
 
191
191
  context 'when organization does not have a custom sender address' do
192
- it { expect(email.from).to eq ['support@mumuki.org'] }
192
+ it { expect(email.from).to eq ['no-reply@mumuki.org'] }
193
193
  end
194
194
 
195
195
  context 'when organization does not have a custom sender address' do
@@ -198,4 +198,11 @@ RSpec.describe UserMailer, type: :mailer do
198
198
  it { expect(email.from).to eq ['info@mumuki.org'] }
199
199
  end
200
200
  end
201
+
202
+ describe 'delete account email' do
203
+ let(:user) { create(:user, delete_account_token: 'SecreT1234', last_organization: central) }
204
+ let(:email) { UserMailer.delete_account(user) }
205
+
206
+ it { expect(email.body.encoded).to include 'central.localmumuki.io/user/delete_confirmation?token=SecreT1234' }
207
+ end
201
208
  end