mumuki-laboratory 7.10.5 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -7
  3. data/Rakefile +9 -2
  4. data/app/assets/javascripts/mumuki_laboratory/application/bridge.js +26 -5
  5. data/app/assets/javascripts/mumuki_laboratory/application/characters.js +3 -1
  6. data/app/assets/javascripts/mumuki_laboratory/application/gamification.js +85 -0
  7. data/app/assets/javascripts/mumuki_laboratory/application/kids.js +160 -334
  8. data/app/assets/javascripts/mumuki_laboratory/application/kindergarten.js +200 -0
  9. data/app/assets/javascripts/mumuki_laboratory/application/primary.js +257 -0
  10. data/app/assets/javascripts/mumuki_laboratory/application/profile.js +31 -16
  11. data/app/assets/javascripts/mumuki_laboratory/application/submission.js +1 -0
  12. data/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js +19 -2
  13. data/app/assets/stylesheets/mumuki_laboratory/application/_errors.scss +1 -3
  14. data/app/assets/stylesheets/mumuki_laboratory/application/_modules.scss +3 -1
  15. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_avatar.scss +21 -0
  16. data/app/assets/stylesheets/mumuki_laboratory/application/modules/{_chapter_show.scss → _content_show.scss} +0 -0
  17. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kindergarten.scss +421 -12
  18. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_medal.scss +48 -0
  19. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_terms.scss +44 -0
  20. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_user_profile.scss +9 -0
  21. data/app/controllers/api/courses_controller.rb +1 -2
  22. data/app/controllers/api/organizations_controller.rb +2 -3
  23. data/app/controllers/api/users_controller.rb +2 -4
  24. data/app/controllers/application_controller.rb +15 -8
  25. data/app/controllers/book_discussions_controller.rb +4 -0
  26. data/app/controllers/invitations_controller.rb +1 -2
  27. data/app/controllers/users_controller.rb +8 -2
  28. data/app/helpers/avatar_helper.rb +11 -3
  29. data/app/helpers/contextualization_result_helper.rb +9 -1
  30. data/app/helpers/kindergarten_helper.rb +5 -0
  31. data/app/helpers/links_helper.rb +8 -0
  32. data/app/helpers/medal_helper.rb +36 -0
  33. data/app/helpers/open_graph_helper.rb +2 -2
  34. data/app/helpers/organization_list_helper.rb +1 -1
  35. data/app/helpers/overlapped_buttons_helper.rb +1 -1
  36. data/app/helpers/page_title_helper.rb +2 -2
  37. data/app/helpers/profile_helper.rb +9 -1
  38. data/app/views/book/_header.html.erb +17 -0
  39. data/app/views/book/show.html.erb +1 -18
  40. data/app/views/discussions/terms.html.erb +10 -0
  41. data/app/views/exercise_solutions/_kids_results.html.erb +1 -1
  42. data/app/views/exercises/show.html.erb +1 -0
  43. data/app/views/invitations/_invitation_form.html.erb +1 -0
  44. data/app/views/layouts/_discussions.html.erb +4 -0
  45. data/app/views/layouts/_error.html.erb +3 -6
  46. data/app/views/layouts/_guide.html.erb +10 -3
  47. data/app/views/layouts/_kindergarten.html.erb +38 -0
  48. data/app/views/layouts/_main.html.erb +3 -1
  49. data/app/views/layouts/_organization_chooser.html.erb +0 -7
  50. data/app/views/layouts/_terms_acceptance_disclaimer.html.erb +6 -0
  51. data/app/views/layouts/application.html.erb +3 -2
  52. data/app/views/layouts/exercise_inputs/layouts/_input_kindergarten.html.erb +27 -27
  53. data/app/views/layouts/modals/_guide_corollary.html.erb +9 -0
  54. data/app/views/layouts/modals/_kindergarten_context.html.erb +30 -0
  55. data/app/views/layouts/modals/_kindergarten_results.html.erb +23 -0
  56. data/app/views/layouts/modals/_kindergarten_results_aborted.html.erb +27 -0
  57. data/app/views/users/_avatar_list.html.erb +6 -2
  58. data/app/views/users/_edit_user_form.html.erb +9 -4
  59. data/app/views/users/_term.html.erb +10 -0
  60. data/app/views/users/_user_form.html.erb +5 -1
  61. data/app/views/users/terms.html.erb +18 -0
  62. data/config/routes.rb +2 -0
  63. data/lib/mumuki/laboratory.rb +1 -1
  64. data/lib/mumuki/laboratory/controllers/current_organization.rb +1 -1
  65. data/lib/mumuki/laboratory/controllers/results_rendering.rb +3 -2
  66. data/lib/mumuki/laboratory/events/events.rb +0 -24
  67. data/lib/mumuki/laboratory/locales/en.yml +16 -5
  68. data/lib/mumuki/laboratory/locales/es-CL.yml +5 -4
  69. data/lib/mumuki/laboratory/locales/es.yml +17 -6
  70. data/lib/mumuki/laboratory/locales/pt.yml +16 -5
  71. data/lib/mumuki/laboratory/version.rb +1 -1
  72. data/spec/capybara_helper.rb +99 -0
  73. data/spec/controllers/exercise_solutions_controller_spec.rb +3 -4
  74. data/spec/dummy/db/schema.rb +37 -1
  75. data/spec/dummy/public/medal/outline.svg +1089 -0
  76. data/spec/features/choose_organization_spec.rb +12 -30
  77. data/spec/features/disable_user_flow_spec.rb +3 -5
  78. data/spec/features/disabled_organization_flow_spec.rb +9 -14
  79. data/spec/features/exercise_flow_spec.rb +2 -2
  80. data/spec/features/guide_reset_spec.rb +1 -1
  81. data/spec/features/guides_flow_spec.rb +1 -1
  82. data/spec/features/home_private_flow_spec.rb +1 -3
  83. data/spec/features/home_public_flow_spec.rb +6 -12
  84. data/spec/features/invitations_flow_spec.rb +2 -2
  85. data/spec/features/login_flow_spec.rb +2 -2
  86. data/spec/features/not_found_private_flow_spec.rb +4 -4
  87. data/spec/features/not_found_public_flow_spec.rb +1 -6
  88. data/spec/features/profile_flow_spec.rb +1 -1
  89. data/spec/helpers/page_title_helper_spec.rb +3 -3
  90. data/spec/javascripts/editors-spec.js +23 -0
  91. data/spec/javascripts/gamification-spec.js +58 -0
  92. data/spec/javascripts/submissions-store-spec.js +139 -6
  93. data/spec/spec_helper.rb +2 -0
  94. data/spec/teaspoon_env.rb +24 -6
  95. metadata +41 -7
@@ -8,7 +8,7 @@ feature 'Choose organization Flow' do
8
8
  let(:user4) { create(:user, permissions: {student: 'immersive-orga/*'}) }
9
9
 
10
10
  before do
11
- %w(pdep central foo immersive-orga).each do |it|
11
+ %w(pdep central foo immersive-orga base).each do |it|
12
12
  create(:organization,
13
13
  name: it,
14
14
  book: create(:book,
@@ -18,14 +18,15 @@ feature 'Choose organization Flow' do
18
18
  end
19
19
  end
20
20
 
21
- before { Organization.find_by_name('immersive-orga').tap { |it| it.immersive = true }.save! }
21
+ before { Organization.locate!('immersive-orga').update!(immersive: true) }
22
+ before { Organization.locate!('central').update!(immersible: true) }
22
23
  before { set_current_user! user }
23
24
 
24
25
  context 'when organization exists' do
25
26
  before { Organization.central.switch! }
26
27
 
27
28
  scenario 'when visiting with implicit subdomain and no permissions' do
28
- set_implicit_central!
29
+ set_subdomain_host! 'central'
29
30
 
30
31
  visit '/'
31
32
 
@@ -33,19 +34,19 @@ feature 'Choose organization Flow' do
33
34
  expect(page).not_to have_text('Do you want to go there?')
34
35
  end
35
36
 
36
- scenario 'when visiting with implicit subdomain and permissions to only one non-immersive organization' do
37
+ scenario 'when visiting immersible orga with permissions to only one non-immersive organization' do
37
38
  set_current_user! user2
38
- set_implicit_central!
39
+ set_subdomain_host! 'central'
39
40
 
40
41
  visit '/'
41
42
 
42
43
  expect(page).to have_text('Sign Out')
43
- expect(page).to have_text('Do you want to go there?')
44
+ expect(page).not_to have_text('Do you want to go there?')
44
45
  end
45
46
 
46
- scenario 'when visiting with implicit subdomain and permissions to only one immersive organization' do
47
+ scenario 'when visiting immersible orga with permissions to only one immersive organization', :navigation_error do
47
48
  set_current_user! user4
48
- set_implicit_central!
49
+ set_subdomain_host! 'central'
49
50
 
50
51
  visit '/'
51
52
 
@@ -54,37 +55,18 @@ feature 'Choose organization Flow' do
54
55
  expect(page).to have_text('immersive-orga')
55
56
  end
56
57
 
57
- scenario 'when visiting with implicit subdomain and permissions to two or more organizations' do
58
+ scenario 'when visiting immersible orga with permissions to many non-immersive organizations' do
58
59
  set_current_user! user3
59
- set_implicit_central!
60
+ set_subdomain_host! 'central'
60
61
 
61
62
  visit '/'
62
63
 
63
64
  expect(page).to have_text('Sign Out')
64
- expect(page).to have_text('Do you want to go there?')
65
- end
66
-
67
- scenario 'when visiting central explicitly' do
68
- set_subdomain_host!('central')
69
-
70
- visit '/'
71
-
72
- expect(page).not_to have_text('Do you want to go there?')
73
- expect(page).not_to have_text('pdep')
74
- end
75
-
76
- scenario 'when visit foo subdomain' do
77
- set_current_user! user3
78
- set_subdomain_host!('foo')
79
-
80
- visit '/'
81
-
82
65
  expect(page).not_to have_text('Do you want to go there?')
83
- expect(page).to have_text('foo')
84
66
  end
85
67
  end
86
68
 
87
- scenario 'when organization does not exist' do
69
+ scenario 'when organization does not exist', :http_response_headers do
88
70
  visit '/'
89
71
 
90
72
  expect(page).to have_http_status(404)
@@ -17,8 +17,7 @@ feature 'disable user flow', organization_workspace: :test do
17
17
  scenario 'enabled visitor' do
18
18
  visit '/'
19
19
 
20
- expect(page).to have_text('ム mumuki')
21
- expect(page).to have_text(current_organization.book.name)
20
+ expect(page_body).to have_text(current_organization.book.name)
22
21
  expect(user.reload.last_organization).to eq current_organization
23
22
  end
24
23
 
@@ -27,8 +26,7 @@ feature 'disable user flow', organization_workspace: :test do
27
26
 
28
27
  visit '/'
29
28
 
30
- expect(page).to_not have_text('ム mumuki')
31
- expect(page).to_not have_text(current_organization.book.name)
32
- expect(page).to have_text('You are trying to visit a permamently disabled or deleted resource')
29
+ expect(page_body).to_not have_text(current_organization.book.name)
30
+ expect(page_body).to have_text('You are trying to visit a permamently disabled or deleted resource')
33
31
  end
34
32
  end
@@ -17,8 +17,7 @@ feature 'disable user flow', organization_workspace: :test do
17
17
  scenario 'enabled organization' do
18
18
  visit '/'
19
19
 
20
- expect(page).to have_text('ム mumuki')
21
- expect(page).to have_text(current_organization.book.name)
20
+ expect(page_body).to have_text(current_organization.book.name)
22
21
  expect(user.reload.last_organization).to eq current_organization
23
22
  end
24
23
 
@@ -29,17 +28,15 @@ feature 'disable user flow', organization_workspace: :test do
29
28
  scenario 'visit /' do
30
29
  visit '/'
31
30
 
32
- expect(page).to_not have_text('ム mumuki')
33
- expect(page).to_not have_text(current_organization.book.name)
34
- expect(page).to have_text(I18n.t(:unprepared_organization_explanation))
31
+ expect(page_body).to_not have_text(current_organization.book.name)
32
+ expect(page_body).to have_text(I18n.t(:unprepared_organization_explanation))
35
33
  end
36
34
 
37
35
  scenario 'visit /user' do
38
36
  visit '/test/user'
39
37
 
40
- expect(page).to_not have_text('ム mumuki')
41
- expect(page).to_not have_text(current_organization.book.name)
42
- expect(page).to have_text(I18n.t(:unprepared_organization_explanation))
38
+ expect(page_body).to_not have_text(current_organization.book.name)
39
+ expect(page_body).to have_text(I18n.t(:unprepared_organization_explanation))
43
40
  end
44
41
 
45
42
  end
@@ -51,17 +48,15 @@ feature 'disable user flow', organization_workspace: :test do
51
48
  scenario 'visit /' do
52
49
  visit '/'
53
50
 
54
- expect(page).to_not have_text('ム mumuki')
55
- expect(page).to_not have_text(current_organization.book.name)
56
- expect(page).to have_text(I18n.t(:disabled_organization_explanation))
51
+ expect(page_body).to_not have_text(current_organization.book.name)
52
+ expect(page_body).to have_text(I18n.t(:disabled_organization_explanation))
57
53
  end
58
54
 
59
55
  scenario 'visit /user' do
60
56
  visit '/test/user'
61
57
 
62
- expect(page).to_not have_text('ム mumuki')
63
- expect(page).to_not have_text(current_organization.book.name)
64
- expect(page).to have_text(I18n.t(:disabled_organization_explanation))
58
+ expect(page_body).to_not have_text(current_organization.book.name)
59
+ expect(page_body).to have_text(I18n.t(:disabled_organization_explanation))
65
60
  end
66
61
 
67
62
  end
@@ -182,7 +182,7 @@ feature 'Exercise Flow', organization_workspace: :test do
182
182
  expect(page.find("#mu-exercise-layout")['value']).to eq('input_right')
183
183
  end
184
184
 
185
- scenario 'visit exercise by id, upload layout, not queriable language' do
185
+ scenario 'visit exercise by id, upload layout, not queriable language', :invalid_selector_error do
186
186
  visit "/exercises/#{problem_5.id}"
187
187
 
188
188
  expect(page).to have_text('Succ5')
@@ -272,7 +272,7 @@ feature 'Exercise Flow', organization_workspace: :test do
272
272
  expect(page).not_to have_xpath("//a[@title='Edit']")
273
273
  end
274
274
 
275
- scenario 'writer should see the edit exercise link' do
275
+ scenario 'writer should see the edit exercise link', :xpath_no_matches do
276
276
  set_current_user! writer
277
277
 
278
278
  visit "/exercises/#{problem_2.id}"
@@ -39,7 +39,7 @@ feature 'Guide Reset Flow', organization_workspace: :test do
39
39
  expect(page).to_not have_xpath(restart_xpath)
40
40
  end
41
41
 
42
- scenario 'visit guide with solutions sent for it' do
42
+ scenario 'visit guide with solutions sent for it', :xpath_no_matches do
43
43
  problem.submit_solution! user
44
44
 
45
45
  visit "/lessons/#{lesson.id}"
@@ -115,7 +115,7 @@ feature 'Guides Flow', organization_workspace: :test do
115
115
  expect(page).not_to have_xpath("//a[@title='Edit']")
116
116
  end
117
117
 
118
- scenario 'writer should see the edit guide link' do
118
+ scenario 'writer should see the edit guide link', :xpath_no_matches do
119
119
  set_current_user! writer
120
120
 
121
121
  visit "/guides/#{lesson.guide.id}"
@@ -45,7 +45,7 @@ feature 'private org' do
45
45
  allow_any_instance_of(ApplicationController).to receive(:from_sessions?).and_return(false)
46
46
  end
47
47
 
48
- scenario 'visitor should raise forbidden error' do
48
+ scenario 'visitor should raise forbidden error', :organization_not_nil do
49
49
  set_current_user! visitor
50
50
 
51
51
  visit '/'
@@ -59,7 +59,6 @@ feature 'private org' do
59
59
  visit '/'
60
60
 
61
61
  expect(student.reload.last_organization).to eq current_organization
62
- expect(page).to have_text('powered by mumuki')
63
62
  expect(page).to have_text(current_organization.description)
64
63
  expect(page).to have_text(current_organization.book.description)
65
64
  end
@@ -70,7 +69,6 @@ feature 'private org' do
70
69
  visit '/'
71
70
 
72
71
  expect(teacher.reload.last_organization).to eq current_organization
73
- expect(page).to have_text('powered by mumuki')
74
72
  expect(page).to have_text(current_organization.description)
75
73
  expect(page).to have_text(current_organization.book.description)
76
74
  end
@@ -17,11 +17,10 @@ feature 'public org', organization_workspace: :test do
17
17
 
18
18
  context 'anonymous visitor' do
19
19
  scenario 'from outside' do
20
- Capybara.current_session.driver.header 'Referer', 'http://google.com'
20
+ set_request_header! 'Referer', 'http://google.com'
21
21
 
22
22
  visit '/'
23
23
 
24
- expect(page).to have_text('ム mumuki')
25
24
  expect(page).to have_text('First Steps')
26
25
  expect(page).to have_selector('.chapter', text: 'Functional Programming')
27
26
  expect(page).to have_text(current_organization.book.name)
@@ -29,11 +28,10 @@ feature 'public org', organization_workspace: :test do
29
28
  end
30
29
 
31
30
  scenario 'from inside' do
32
- Capybara.current_session.driver.header 'Referer', 'http://en.mumuki.io/exercises/1'
31
+ set_request_header! 'Referer', 'http://en.mumuki.io/exercises/1'
33
32
 
34
33
  visit '/'
35
34
 
36
- expect(page).to have_text('ム mumuki')
37
35
  expect(page).to have_text('First Steps')
38
36
  expect(page).to have_selector('.chapter', text: 'Functional Programming')
39
37
  expect(page).to have_text(current_organization.book.name)
@@ -46,11 +44,10 @@ feature 'public org', organization_workspace: :test do
46
44
  before { exam.authorize! user }
47
45
 
48
46
  scenario 'from outside' do
49
- Capybara.current_session.driver.header 'Referer', 'http://google.com'
47
+ set_request_header! 'Referer', 'http://google.com'
50
48
 
51
49
  visit '/'
52
50
 
53
- expect(page).to have_text('ム mumuki')
54
51
  expect(page).to have_text('First Steps')
55
52
  expect(page).to have_selector('.chapter', text: 'Functional Programming')
56
53
  expect(page).to have_text(current_organization.book.name)
@@ -59,11 +56,10 @@ feature 'public org', organization_workspace: :test do
59
56
  end
60
57
 
61
58
  scenario 'from inside' do
62
- Capybara.current_session.driver.header 'Referer', 'http://en.mumuki.io/exercises/1'
59
+ set_request_header! 'Referer', 'http://en.mumuki.io/exercises/1'
63
60
 
64
61
  visit '/'
65
62
 
66
- expect(page).to have_text('ム mumuki')
67
63
  expect(page).to have_text('First Steps')
68
64
  expect(page).to have_selector('.chapter', text: 'Functional Programming')
69
65
  expect(page).to_not have_selector('.chapter.mu-locked', text: 'Functional Programming')
@@ -80,11 +76,10 @@ feature 'public org', organization_workspace: :test do
80
76
  before { exam.authorize! user }
81
77
 
82
78
  scenario 'from inside' do
83
- Capybara.current_session.driver.header 'Referer', 'http://google.com'
79
+ set_request_header! 'Referer', 'http://google.com'
84
80
 
85
81
  visit '/'
86
82
 
87
- expect(page).to have_text('ム mumuki')
88
83
  expect(page).to have_text('First Steps')
89
84
  expect(page).to have_selector('.chapter.mu-locked', text: 'Functional Programming')
90
85
  expect(page).to have_text(current_organization.book.name)
@@ -95,11 +90,10 @@ feature 'public org', organization_workspace: :test do
95
90
  context 'incognito user' do
96
91
  before { current_organization.update! incognito_mode_enabled: true }
97
92
  scenario 'from inside' do
98
- Capybara.current_session.driver.header 'Referer', 'http://google.com'
93
+ set_request_header! 'Referer', 'http://google.com'
99
94
 
100
95
  visit '/'
101
96
 
102
- expect(page).to have_text('ム mumuki')
103
97
  expect(page).to have_text('First Steps')
104
98
  expect(page).to have_selector('.chapter', text: 'Functional Programming')
105
99
  expect(page).to have_text(current_organization.book.name)
@@ -9,7 +9,7 @@ feature 'Invitations Flow', organization_workspace: :test do
9
9
  create(:lesson, guide: guide)
10
10
  ]) }
11
11
  let(:book) { organization.book }
12
-
12
+
13
13
  before do
14
14
  book.update! chapters: [chapter]
15
15
  end
@@ -33,7 +33,7 @@ feature 'Invitations Flow', organization_workspace: :test do
33
33
  context 'with existing memberships' do
34
34
  let(:permissions) { { student: 'test/nodejs' } }
35
35
 
36
- scenario 'visit invitation, already joined' do
36
+ scenario 'visit invitation, already joined', :navigation_error do
37
37
  visit '/join/invitacionAlNodejs'
38
38
  expect(page).to have_text('Start Practicing!')
39
39
  end
@@ -24,7 +24,7 @@ feature 'Login Flow', organization_workspace: :test do
24
24
  expect(page).to have_text('Sign Out')
25
25
  end
26
26
 
27
- scenario 'can login and keeps session' do
27
+ scenario 'can login and keeps session', :navigation_error do
28
28
  visit '/'
29
29
 
30
30
  click_on 'Sign in'
@@ -45,7 +45,7 @@ feature 'Login Flow', organization_workspace: :test do
45
45
  expect(page).to have_text('awesomeRubyGuide')
46
46
  end
47
47
 
48
- scenario 'can logout' do
48
+ scenario 'can logout', :element_not_interactable_error do
49
49
  visit '/'
50
50
 
51
51
  click_on 'Sign in'
@@ -22,8 +22,8 @@ feature 'not found private on app', organization_workspace: :base do
22
22
  expect(page).to have_text 'You may have mistyped the address or the page may have moved'
23
23
  end
24
24
 
25
- scenario 'api without authorization' do
26
- Capybara.current_session.driver.header 'Authorization', "Bearer #{student_api_client.token}"
25
+ scenario 'api without authorization', :json_eq_error do
26
+ set_request_header! 'Authorization', "Bearer #{student_api_client.token}"
27
27
 
28
28
  visit '/api/nonexistentroute'
29
29
 
@@ -33,8 +33,8 @@ feature 'not found private on app', organization_workspace: :base do
33
33
  ' with permissions !student:central/*;teacher:;headmaster:;janitor:;admin:;owner:']
34
34
  end
35
35
 
36
- scenario 'api with authentication' do
37
- Capybara.current_session.driver.header 'Authorization', "Bearer #{admin_api_client.token}"
36
+ scenario 'api with authentication', :json_eq_error do
37
+ set_request_header! 'Authorization', "Bearer #{admin_api_client.token}"
38
38
 
39
39
  visit '/api/nonexistentroute'
40
40
 
@@ -2,17 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  feature 'not found public on app' do
4
4
  let!(:central) { create(:organization, name: 'central') }
5
+ let!(:base) { create(:organization, name: 'base') }
5
6
  let!(:some_orga) { create(:public_organization, name: 'someorga', profile: profile) }
6
7
 
7
8
  let(:profile) { Mumuki::Domain::Organization::Profile.parse json }
8
9
  let(:json) { { contact_email: 'some@email.com', locale: 'en', errors_explanations: { 404 => 'Some explanation'} } }
9
10
 
10
- scenario 'when routes does not exist in implicit central' do
11
- visit '/foo'
12
-
13
- expect(page).to have_text('You may have mistyped the address')
14
- end
15
-
16
11
  scenario 'when route does not exist in explicit central' do
17
12
  set_subdomain_host! 'test'
18
13
 
@@ -49,7 +49,7 @@ feature 'Profile Flow', organization_workspace: :test do
49
49
  expect(page).to have_text('Please complete your profile data to continue!')
50
50
  end
51
51
 
52
- scenario 'is able to log out' do
52
+ scenario 'is able to log out', :element_not_interactable_error do
53
53
  click_on 'Sign in'
54
54
  set_automatic_login! false
55
55
 
@@ -12,8 +12,8 @@ describe PageTitleHelper, organization_workspace: :test do
12
12
 
13
13
  before { reindex_current_organization! }
14
14
 
15
- it { expect(page_title nil).to eq 'Mumuki - test - Improve your programming skills' }
16
- it { expect(page_title Problem.new).to eq 'Mumuki - test - Improve your programming skills' }
17
- it { expect(page_title exercise).to eq 'C1: A Guide - An Exercise - Mumuki - test' }
15
+ it { expect(page_title nil).to eq 'test' }
16
+ it { expect(page_title Problem.new).to eq 'test' }
17
+ it { expect(page_title exercise).to eq 'C1: A Guide - An Exercise - test' }
18
18
  end
19
19
  end
@@ -47,6 +47,29 @@ describe('editors', () => {
47
47
  expect(mumuki.editors.getSubmission()).toEqual({"solution[content]":"the solution"});
48
48
  });
49
49
 
50
+
51
+ it('reads the form if no sources and exercise is multifile', () => {
52
+ $('body').html(`
53
+ <form role="form" class="new_solution">
54
+ <div class="field form-group editor-code">
55
+ <textarea
56
+ class="form-control editor"
57
+ data-editor-language="html"
58
+ name="solution[content[index.html]]"
59
+ id="solution_content[index.html]">some html</textarea>
60
+ <textarea
61
+ class="form-control editor"
62
+ data-editor-language="css"
63
+ name="solution[content[receta.css]]"
64
+ id="solution_content[receta.css]">some css</textarea>
65
+ </div>
66
+ </form>`)
67
+ expect(mumuki.editors.getSubmission()).toEqual({
68
+ "solution[content[index.html]]": "some html",
69
+ "solution[content[receta.css]]": "some css"
70
+ });
71
+ });
72
+
50
73
  it('produces empty submission if no form nor sources', () => {
51
74
  $('body').html(``);
52
75
  expect(mumuki.editors.getSubmission()).toEqual({});
@@ -0,0 +1,58 @@
1
+ describe('gamification', () => {
2
+ describe('formula', () => {
3
+ it('has positive coefficients', () => {
4
+ expect(mumuki.gamification.Formula.QUADRATIC_COEFFICIENT).toBeGreaterThan(0);
5
+ expect(mumuki.gamification.Formula.LINEAR_COEFFICIENT).toBeGreaterThan(0);
6
+ });
7
+
8
+ it('has negative constant term', () => {
9
+ expect(mumuki.gamification.Formula.CONSTANT_TERM).toBeLessThan(0);
10
+ });
11
+ });
12
+
13
+ describe('level progression', () => {
14
+ describe('exp to level up', () => {
15
+ it('takes 175 exp points to go from level 1 to 2', () => {
16
+ expect(new mumuki.gamification.LevelProgression(0).expToLevelUp()).toBe(175);
17
+ });
18
+
19
+ it('takes 225 points to go from level 2 to 3', () => {
20
+ expect(new mumuki.gamification.LevelProgression(175).expToLevelUp()).toBe(225);
21
+ });
22
+ });
23
+
24
+ describe('current level', () => {
25
+ it('is at level 1 when 0 exp points', () => {
26
+ expect(new mumuki.gamification.LevelProgression(0).currentLevel()).toBe(1);
27
+ });
28
+
29
+ it('is at level 5 when 1000 exp points', () => {
30
+ expect(new mumuki.gamification.LevelProgression(1000).currentLevel()).toBe(5);
31
+ });
32
+ });
33
+
34
+ describe('triggers level change', () => {
35
+ it('does not trigger when small exp difference', () => {
36
+ expect(new mumuki.gamification.LevelProgression(1000).triggersLevelChange(1050)).toBe(false);
37
+ });
38
+
39
+ it('does trigger when big exp difference', () => {
40
+ expect(new mumuki.gamification.LevelProgression(1000).triggersLevelChange(1500)).toBe(true);
41
+ });
42
+ });
43
+
44
+ describe('current level progress', () => {
45
+ it('is at 0% when just leveled up', () => {
46
+ expect(new mumuki.gamification.LevelProgression(675).currentLevelProgress()).toBe(0);
47
+ });
48
+
49
+ it('is at 50% when halfway through', () => {
50
+ expect(new mumuki.gamification.LevelProgression(837).currentLevelProgress()).toBeCloseTo(0.50, 0.01);
51
+ });
52
+
53
+ it('is at 99% when almost there', () => {
54
+ expect(new mumuki.gamification.LevelProgression(999).currentLevelProgress()).toBeCloseTo(0.99, 0.01);
55
+ });
56
+ });
57
+ });
58
+ });