mumuki-laboratory 7.6.2 → 7.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +203 -2
  3. data/Rakefile +9 -0
  4. data/app/assets/javascripts/mumuki_laboratory/application.js +0 -1
  5. data/app/assets/javascripts/mumuki_laboratory/application/alias-modes.js +1 -1
  6. data/app/assets/javascripts/mumuki_laboratory/application/assets-loader.js +1 -1
  7. data/app/assets/javascripts/mumuki_laboratory/application/bridge.js +82 -47
  8. data/app/assets/javascripts/mumuki_laboratory/application/button.js +90 -1
  9. data/app/assets/javascripts/mumuki_laboratory/application/codemirror-builder.js +28 -25
  10. data/app/assets/javascripts/mumuki_laboratory/application/codemirror.js +8 -9
  11. data/app/assets/javascripts/mumuki_laboratory/application/confirmation.js +2 -2
  12. data/app/assets/javascripts/mumuki_laboratory/application/console.js +41 -43
  13. data/app/assets/javascripts/mumuki_laboratory/application/csrf-token.js +9 -12
  14. data/app/assets/javascripts/mumuki_laboratory/application/custom-editor.js +46 -8
  15. data/app/assets/javascripts/mumuki_laboratory/application/discussions.js +15 -16
  16. data/app/assets/javascripts/mumuki_laboratory/application/editors.js +104 -0
  17. data/app/assets/javascripts/mumuki_laboratory/application/elipsis.js +5 -4
  18. data/app/assets/javascripts/mumuki_laboratory/application/exercise.js +32 -0
  19. data/app/assets/javascripts/mumuki_laboratory/application/inputs.js +4 -2
  20. data/app/assets/javascripts/mumuki_laboratory/application/interval.js +2 -4
  21. data/app/assets/javascripts/mumuki_laboratory/application/kids.js +74 -37
  22. data/app/assets/javascripts/mumuki_laboratory/application/load-analytics.js +1 -1
  23. data/app/assets/javascripts/mumuki_laboratory/application/load-error-svg.js +1 -1
  24. data/app/assets/javascripts/mumuki_laboratory/application/messages.js +2 -2
  25. data/app/assets/javascripts/mumuki_laboratory/application/multiple-choice.js +1 -1
  26. data/app/assets/javascripts/mumuki_laboratory/application/multiple-scenarios.js +3 -6
  27. data/app/assets/javascripts/mumuki_laboratory/application/pin.js +3 -5
  28. data/app/assets/javascripts/mumuki_laboratory/application/progress.js +27 -6
  29. data/app/assets/javascripts/mumuki_laboratory/application/results-renderer.js +60 -0
  30. data/app/assets/javascripts/mumuki_laboratory/application/speech-bubble-renderer.js +12 -5
  31. data/app/assets/javascripts/mumuki_laboratory/application/submission.js +122 -55
  32. data/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js +93 -0
  33. data/app/assets/javascripts/mumuki_laboratory/application/sync-mode.js +75 -0
  34. data/app/assets/javascripts/mumuki_laboratory/application/timer.js +5 -6
  35. data/app/assets/javascripts/mumuki_laboratory/application/tooltip.js +1 -1
  36. data/app/assets/javascripts/mumuki_laboratory/application/upload.js +1 -1
  37. data/app/assets/javascripts/mumuki_laboratory/application/user.js +1 -1
  38. data/app/assets/stylesheets/mumuki_laboratory/application/_modules.scss +1 -0
  39. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +43 -5
  40. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_gs-board.scss +3 -0
  41. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kids.scss +3 -4
  42. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_kindergarten.scss +55 -0
  43. data/app/controllers/application_controller.rb +1 -0
  44. data/app/controllers/assets_controller.rb +2 -0
  45. data/app/controllers/concerns/with_authorization.rb +4 -0
  46. data/app/controllers/concerns/with_user_discussion_validation.rb +14 -0
  47. data/app/controllers/discussions_controller.rb +6 -14
  48. data/app/controllers/discussions_messages_controller.rb +10 -1
  49. data/app/controllers/exercise_solutions_controller.rb +4 -2
  50. data/app/helpers/application_helper.rb +9 -5
  51. data/app/helpers/discussions_helper.rb +37 -23
  52. data/app/helpers/exercise_input_helper.rb +1 -1
  53. data/app/helpers/{locale_helper.rb → globals_helper.rb} +6 -2
  54. data/app/helpers/icons_helper.rb +3 -3
  55. data/app/mailers/user_mailer.rb +24 -11
  56. data/app/views/book/show.html.erb +1 -1
  57. data/app/views/book_discussions/index.html.erb +3 -3
  58. data/app/views/discussions/_message.html.erb +20 -8
  59. data/app/views/discussions/index.html.erb +0 -1
  60. data/app/views/discussions/new.html.erb +33 -0
  61. data/app/views/discussions/show.html.erb +18 -46
  62. data/app/views/exercise_solutions/_contextualization_results_container.html.erb +1 -1
  63. data/app/views/exercise_solutions/_results_title.html.erb +2 -2
  64. data/app/views/exercises/_read_only.html.erb +33 -6
  65. data/app/views/exercises/show.html.erb +2 -0
  66. data/app/views/layouts/_copyright.html.erb +1 -1
  67. data/app/views/layouts/_discussions.html.erb +21 -3
  68. data/app/views/layouts/_main.html.erb +1 -2
  69. data/app/views/layouts/_progress.html.erb +1 -1
  70. data/app/views/layouts/_progress_bar.html.erb +7 -1
  71. data/app/views/layouts/_test_results.html.erb +1 -1
  72. data/app/views/layouts/application.html.erb +1 -1
  73. data/app/views/layouts/exercise_inputs/editors/_custom.html.erb +1 -1
  74. data/app/views/layouts/exercise_inputs/forms/_kids_form.html.erb +1 -1
  75. data/app/views/layouts/exercise_inputs/forms/_problem_form.html.erb +1 -1
  76. data/app/views/layouts/exercise_inputs/layouts/_input_bottom.html.erb +1 -1
  77. data/app/views/layouts/exercise_inputs/layouts/_input_kindergarten.html.erb +40 -0
  78. data/app/views/layouts/exercise_inputs/layouts/{_input_kids.html.erb → _input_primary.html.erb} +1 -1
  79. data/app/views/layouts/exercise_inputs/layouts/_input_right.html.erb +1 -1
  80. data/app/views/layouts/modals/_kids_context.html.erb +1 -8
  81. data/app/views/user_mailer/1st_reminder.html.erb +1 -1
  82. data/app/views/user_mailer/1st_reminder.text.erb +1 -1
  83. data/app/views/user_mailer/2nd_reminder.html.erb +1 -1
  84. data/app/views/user_mailer/2nd_reminder.text.erb +1 -1
  85. data/app/views/user_mailer/3rd_reminder.html.erb +1 -1
  86. data/app/views/user_mailer/3rd_reminder.text.erb +1 -1
  87. data/app/views/user_mailer/no_submissions_reminder.html.erb +1 -1
  88. data/app/views/user_mailer/no_submissions_reminder.text.erb +1 -1
  89. data/config/routes.rb +2 -1
  90. data/lib/mumuki/laboratory/controllers.rb +1 -0
  91. data/lib/mumuki/laboratory/controllers/incognito_mode.rb +28 -0
  92. data/lib/mumuki/laboratory/controllers/results_rendering.rb +1 -2
  93. data/lib/mumuki/laboratory/locales/en.yml +14 -6
  94. data/lib/mumuki/laboratory/locales/es-CL.yml +292 -0
  95. data/lib/mumuki/laboratory/locales/es.yml +13 -5
  96. data/lib/mumuki/laboratory/locales/pt.yml +12 -6
  97. data/lib/mumuki/laboratory/version.rb +1 -1
  98. data/spec/controllers/confirmations_controller_spec.rb +1 -1
  99. data/spec/controllers/discussions_messages_controller_spec.rb +73 -0
  100. data/spec/controllers/exercise_solutions_controller_spec.rb +41 -6
  101. data/spec/dummy/db/schema.rb +13 -1
  102. data/spec/features/chapter_spec.rb +17 -0
  103. data/spec/features/discussion_flow_spec.rb +190 -0
  104. data/spec/features/exercise_flow_spec.rb +48 -3
  105. data/spec/features/home_public_flow_spec.rb +16 -0
  106. data/spec/features/menu_bar_spec.rb +88 -7
  107. data/spec/helpers/breadcrumbs_helper_spec.rb +1 -1
  108. data/spec/javascripts/bridge-spec.js +5 -0
  109. data/spec/javascripts/csrf-token-spec.js +7 -0
  110. data/spec/javascripts/editors-spec.js +54 -0
  111. data/spec/javascripts/elipsis-spec.js +25 -0
  112. data/spec/javascripts/exercise-spec.js +22 -0
  113. data/spec/javascripts/global-spec.js +6 -0
  114. data/spec/javascripts/results-renderers-spec.js +17 -0
  115. data/spec/javascripts/spec-helper.js +34 -0
  116. data/spec/javascripts/speech-bubble-renderer-spec.js +11 -0
  117. data/spec/javascripts/submissions-store-spec.js +44 -0
  118. data/spec/javascripts/sync-mode-spec.js +15 -0
  119. data/spec/javascripts/timeout-spec.js +5 -0
  120. data/spec/javascripts/timer-spec.js +5 -0
  121. data/spec/mailers/user_mailer_spec.rb +18 -3
  122. data/spec/teaspoon_env.rb +193 -0
  123. metadata +50 -11
  124. data/app/helpers/version_helper.rb +0 -5
  125. data/app/views/layouts/modals/_new_discussion.html.erb +0 -27
  126. data/vendor/assets/javascripts/hotjar.js +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fecc1d7f623d854c2554619adfc59748ca2b048de16a11397189c951c6243212
4
- data.tar.gz: d65801b409606fb0fdf446246ba28c0af7a264ab42377f4632300890a9ae66e6
3
+ metadata.gz: cf3186d3e4ad3367ccf34396dfde31138bb255456b8cf5e5593a76e908545434
4
+ data.tar.gz: 7e3aa354499be05940fe21355f7e4a45ae933dab0a989cd8db99b48e564c8f12
5
5
  SHA512:
6
- metadata.gz: 1f77c9e8dcf0c095871c903460ff37cfe6c3603a5855c31f1d02cafe97015a6e6bd5f884b51363e58bc8324cbc56e9b6aed654610275e141a444b01813d609e5
7
- data.tar.gz: ecf2f4a616198b6cf6565a73d039c3bb88a421e25a644947ac2a3170f24c190901930ab354b2812eac01a843200dec6b6ebb9064469abe64677983e8f9e02e31
6
+ metadata.gz: e91791ad61d5843960950276383f74330b0eec046f475400c21fdb89af80f044fa3902a7a86e82d1dc3e130f30716c62e23d79f5ea87c8416c9140e550414819
7
+ data.tar.gz: 528f209da31776d4c01be41b92c2218bcbb6dc98175809a05062c3b077e1dc0ca8951b034e44a9a0f3cca93bbba371fd0f14da18bb56e15fac9ddb3b2e211455
data/README.md CHANGED
@@ -135,6 +135,21 @@ rails s
135
135
  bundle exec rspec
136
136
  ```
137
137
 
138
+ ## Running JS tests
139
+
140
+ > You need first to download [geckodriver](https://github.com/mozilla/geckodriver/releases/download/v0.27.0/geckodriver-v0.27.0-linux64.tar.gz), uncrompress
141
+ > it and add it to your path
142
+
143
+ ```bash
144
+ MOZ_HEADLESS=1 bundle exec rake teaspoon
145
+ ```
146
+
147
+ ## Running `eslint`
148
+
149
+ ```bash
150
+ yarn run lint
151
+ ```
152
+
138
153
  ## JavaScript API Docs
139
154
 
140
155
  In order to be customized by runners, Laboratory exposes the following selectors and methods
@@ -158,7 +173,9 @@ which are granted to be safe and stable.
158
173
  * `.mu-kids-submit-button`
159
174
  * `.mu-multiple-scenarios`
160
175
  * `.mu-scenarios`
176
+ * `.mu-submit-button`
161
177
  * `#mu-actual-state-text`
178
+ * `#mu-${languageName}-custom-editor`
162
179
  * `#mu-custom-editor-default-value`
163
180
  * `#mu-custom-editor-extra`
164
181
  * `#mu-custom-editor-test`
@@ -170,7 +187,6 @@ which are granted to be safe and stable.
170
187
  * `.mu-kids-gbs-board-initial`: Use `.mu-initial-state` instead
171
188
  * `.mu-state-final`: Use `.mu-final-state` instead
172
189
  * `.mu-state-initial`: Use `.mu-initial-state` instead
173
- * `#kids-context`: Use `.mu-kids-context` instead
174
190
  * `#kids-results-aborted`: Use `.mu-kids-results-aborted` instead
175
191
  * `#kids-results`: Use `.mu-kids-results` instead
176
192
 
@@ -178,6 +194,8 @@ which are granted to be safe and stable.
178
194
 
179
195
  * `mumuki.bridge.Laboratory`
180
196
  * `.runTests`
197
+ * `mumuki.CustomEditor`
198
+ * `addSource`
181
199
  * `mumuki.editor`
182
200
  * `formatContent`
183
201
  * `reset`
@@ -191,10 +209,15 @@ which are granted to be safe and stable.
191
209
  * `scaleBlocksArea`
192
210
  * `scaleState`
193
211
  * `showResult`
212
+ * `showContext`
194
213
  * `mumuki.renderers`
195
214
  * `SpeechBubbleRenderer`
196
215
  * `renderSpeechBubbleResultItem`
197
216
  * `mumuki.locale`
217
+ * `mumuki.exercise`
218
+ * `id`: the `id` of the currently loaded exercise, if any
219
+ * `layout`: the `layout` of the currently loaded exercise, if any
220
+ * `mumuki.incognitoUser`: whether the current user is an incognito user
198
221
  * `mumuki.MultipleScenarios`
199
222
  * `scenarios`
200
223
  * `currentScenarioIndex`
@@ -205,6 +228,9 @@ which are granted to be safe and stable.
205
228
  * `setUpDeleteFiles`
206
229
  * `setUpDeleteFile`
207
230
  * `updateButtonsVisibility`
231
+ * `mumuki.submission`
232
+ * `processSolution`
233
+ * `registerContentSyncer`
208
234
  * `mumuki.version`
209
235
 
210
236
  ### Bridge Response Format
@@ -213,7 +239,6 @@ which are granted to be safe and stable.
213
239
  {
214
240
  "status": "passed|passed_with_warnings|failed",
215
241
  "guide_finished_by_solution": "boolean",
216
- "class_for_progress_list_item": "string",
217
242
  "html": "string",
218
243
  "remaining_attempts_html": "string" ,
219
244
  "title_html": "string", // kids-only
@@ -243,6 +268,182 @@ which are granted to be safe and stable.
243
268
  2. Laboratory Kids Layout Initialization
244
269
  3. Runner Editor HTML
245
270
 
271
+ ## Custom editors
272
+
273
+ Mumuki provides several editor types: code editors, multiple choice, file upload, and so on.
274
+ However, some runners will require custom editors in order to provide better ways of entering
275
+ solutions.
276
+
277
+ The process to do so is not difficult, but tricky, since there are a few hooks you need to implement. Let's look at them:
278
+
279
+ ### 1. Before state: adding layout assets
280
+
281
+ If you need to provide a custom editor, chances are that you also need to provide assets to augment the layout, e.g. providing ways
282
+ to render some custom components on descriptions or corollaries. That code will be included first.
283
+
284
+ In order to do that, add to your runner the layout html, css and js code. Layout code has no further requirements. It can customize any public selector previously.
285
+
286
+ Although it is not required, it is recommended that your layout code works with any of the mumuki layouts:
287
+
288
+ * `input_right`
289
+ * `input_bottom`
290
+ * `input_primary`
291
+ * `input_kindergarten`
292
+
293
+ :warning: Not all the selectors will be available to all layouts.
294
+
295
+ Then expose code in the `MetadataHook`:
296
+
297
+ ```ruby
298
+ class ... < Mumukit::Hook
299
+ def metadata
300
+ {
301
+ layout_assets_urls: {
302
+ js: [
303
+ 'assets/....'
304
+ ],
305
+ css: [
306
+ 'assets/....'
307
+ ],
308
+ html: [
309
+ 'assets/....'
310
+ ]
311
+ }
312
+ }
313
+ end
314
+ end
315
+ ```
316
+
317
+ Finally, it is _recommended_ that you layout code calls `mumuki.assetsLoadedFor('layout')` when fully loaded.
318
+
319
+ That's it!
320
+
321
+ ### 2. Adding custom editor assets
322
+
323
+ The process for registering custom editors is more involving.
324
+
325
+ #### 2.1 Add your assets and expose them
326
+
327
+ Add your js, css and html assets to your runner, and expose them in `MetadataHook`:
328
+
329
+ ```ruby
330
+ class ... < Mumukit::Hook
331
+ def metadata
332
+ {
333
+ editor_assets_urls: {
334
+ js: [
335
+ 'assets/....'
336
+ ],
337
+ css: [
338
+ 'assets/....'
339
+ ],
340
+ html: [
341
+ 'assets/....'
342
+ ]
343
+ }
344
+ }
345
+ end
346
+ end
347
+ ```
348
+
349
+ These assets will only be loaded when the editor `custom` is used.
350
+
351
+ #### 2.2 Add your components to the custom editor
352
+
353
+ Using JavaScript, append your components the custom-editor root, which can be found using the following selectors:
354
+
355
+ * `mu-${languageName}-custom-editor`
356
+ * `#mu-${languageName}-custom-editor`
357
+ * `.mu-${languageName}-custom-editor`
358
+
359
+ ```javascript
360
+ $('#mu-mylang-custom-editor').append(/* ... */)
361
+ ```
362
+
363
+ #### 2.3 Extract the test
364
+
365
+ If necessary, read the test definition from `#mu-custom-editor-test`, and plump into your custom components
366
+
367
+ ```javascript
368
+ const test = $('#mu-custom-editor-test').val()
369
+ //...use test...
370
+ ```
371
+
372
+ #### 2.4 Exposing your content
373
+
374
+ Before sending a submission, mumuki needs to be able to your read you editor components
375
+ contents. There are two different approaches:
376
+
377
+ * Register a syncer that writes `#mu-custom-editor-value` or any other custom editor selectors
378
+ * Add one or more content sources
379
+
380
+ ```javascript
381
+ // simplest method - you can register just one
382
+ mumuki.editors.registerContentSyncer(() => {
383
+ // ... write here your custom component content...
384
+ $('#mu-custom-editor-value').val(/* ... */);
385
+ });
386
+
387
+ // alternate method
388
+ // you can register many sources
389
+ mumuki.editors.addCustomSource({
390
+ getContent() {
391
+ return { name: "solution[content]", value: /* ... */ } ;
392
+ }
393
+ });
394
+ ```
395
+
396
+ #### 2.5 Optional: Sending your solution to the server programmatically
397
+
398
+ Your solution will be automatically sent to the client when the submit button is pressed. However,
399
+ if you need to trigger submission process programmatically, call `mumuki.submission.processSolution`:
400
+
401
+ ```javascript
402
+ mumuki.submission.processSolution({solution: {content: /* ... */}});
403
+ ```
404
+
405
+ #### 2.6 Optional: customizing your submit button
406
+
407
+ You can alternatively override the default submit button UI and behaviour, by replacing it with a custom component. In order to
408
+ do that, override the `.mu-submit-button` or the kids-specific `.mu-kids-submit-button`:
409
+
410
+ ```javascript
411
+ $(".mu-submit-button").html(/* ... */);
412
+ ```
413
+
414
+ However, doing this is tricky, since you will need to manually update the UI and connecting to the server. See:
415
+
416
+ * `mumuki.kids.showResult`
417
+ * `mumuki.bridge.Laboratory.runTests`
418
+ * `mumuki.updateProgressBarAndShowModal`
419
+
420
+ #### 2.7 Register kids scalers
421
+
422
+ Kids layouts have some special areas:
423
+
424
+ * _state area_: its display initial and/or final states of the exercise
425
+ * _blocks area_: a workspace that contains the building blocks of the solution - which are not necessary programming or blockly blocks, actually
426
+
427
+ If you want to support kids layouts, you **need** to register scalers that will be called when device is resized. Skip this step otherwise.
428
+
429
+ ```javascript
430
+ mumuki.kids.registerStateScaler(($state, fullMargin, preferredWidth, preferredHeight) => {
431
+ // ... resize your components ...
432
+ });
433
+
434
+ mumuki.kids.registerBlocksAreaScaler(($blocks) => {
435
+ // ... resize your components ...
436
+ });
437
+ ```
438
+
439
+ #### 2.8 Notify when your assets have been loaded
440
+
441
+ In order to remove loading spinners, you will need to call `mumuki.assetsLoadedFor` when your code is ready.
442
+
443
+ ```javascript
444
+ mumuki.assetsLoadedFor('editor');
445
+ ```
446
+
246
447
  ## Transparent Navigation API Docs
247
448
 
248
449
  In order to be able to link content, laboratory exposes slug-based routes that will redirect to the actual
data/Rakefile CHANGED
@@ -27,6 +27,15 @@ require 'rspec/core/rake_task'
27
27
  desc "Run all specs in spec directory (excluding plugin specs)"
28
28
  RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
29
29
 
30
+ desc "Force development environment, required by javascript specs"
31
+ task :development do
32
+ ENV['RACK_ENV'] = 'development'
33
+ ENV['RAILS_ENV'] = 'development'
34
+ end
35
+
36
+ desc "Run the javascript specs"
37
+ task teaspoon: [:development, "app:teaspoon"]
38
+
30
39
  task default: :spec
31
40
 
32
41
 
@@ -27,7 +27,6 @@
27
27
  //= require codemirror-autorefresh
28
28
  //= require codemirror-modes
29
29
  //= require analytics
30
- //= require hotjar
31
30
  //= require muvment
32
31
 
33
32
  //= require_tree ./application
@@ -1,4 +1,4 @@
1
- mumuki.load(function () {
1
+ mumuki.load(() => {
2
2
 
3
3
  function CodeMirrorAlias(alias, current) {
4
4
  CodeMirror.defineMIME(alias, CodeMirror.mimeModes[current]);
@@ -1,4 +1,4 @@
1
- const assetsLoader = {
1
+ var assetsLoader = {
2
2
  layout: {
3
3
  onLoadingStarted: function () {
4
4
 
@@ -1,68 +1,103 @@
1
- var mumuki = mumuki || {};
1
+ /**
2
+ * @typedef {"errored"|"failed"|"passed_with_warnings"|"passed"|"pending"|"aborted"} SubmissionStatus
3
+ */
2
4
 
3
- (function (mumuki) {
4
- var lastSubmission = {};
5
+ /**
6
+ * @typedef {{
7
+ * status: SubmissionStatus,
8
+ * test_results: [{status: SubmissionStatus, title: string}]
9
+ * }} SubmissionClientResult
10
+ */
5
11
 
6
- function Laboratory(exerciseId){
7
- this.exerciseId = exerciseId;
8
- }
12
+ /**
13
+ * @typedef {{
14
+ * status: SubmissionStatus,
15
+ * class_for_progress_list_item?: string,
16
+ * guide_finished_by_solution?: boolean
17
+ * }} SubmissionResult
18
+ */
9
19
 
10
- function asString(json){
11
- return JSON.stringify(json);
12
- }
20
+ /**
21
+ * @typedef {object} Solution
22
+ */
13
23
 
14
- function sameAsLastSolution(newSolution){
15
- return asString(lastSubmission.content) === asString(newSolution);
16
- }
24
+ /**
25
+ * @typedef {{
26
+ * "solution[content]"?:string,
27
+ * solution?: Solution,
28
+ * client_result?: SubmissionClientResult
29
+ * }} Submission
30
+ */
17
31
 
18
- function lastSubmissionFinishedSuccessfully(){
19
- return lastSubmission.result && lastSubmission.result.status !== 'aborted';
20
- }
21
-
22
- function sendNewSolution(solution){
23
- var token = new mumuki.CsrfToken();
24
- var request = token.newRequest({
25
- type: 'POST',
26
- url: window.location.origin + window.location.pathname + '/solutions' + window.location.search,
27
- data: solution
28
- });
29
-
30
- return $.ajax(request).done(function (result) {
31
- lastSubmission = { content: solution, result: result };
32
- });
33
- }
32
+ /**
33
+ * @typedef {{submission?: Submission, result?: SubmissionResult}} SubmissionAndResult
34
+ */
34
35
 
35
- mumuki.load(function () {
36
- lastSubmission = {};
37
- });
38
-
39
- Laboratory.prototype = {
36
+ mumuki.bridge = (() => {
40
37
 
38
+ class Laboratory {
41
39
  // ==========
42
40
  // Public API
43
41
  // ==========
44
42
 
45
- // Runs tests for the current exercise using the given submission
46
- // content.
47
- runTests: function(content) {
43
+ /**
44
+ * Runs tests for the current exercise using the given submission
45
+ * content.
46
+ *
47
+ * @param {object} content the content object
48
+ * */
49
+ runTests(content) {
48
50
  return this._submitSolution({ solution: content });
49
- },
51
+ }
50
52
 
51
53
  // ===========
52
54
  // Private API
53
55
  // ===========
54
56
 
55
- _submitSolution: function (solution) {
56
- if(lastSubmissionFinishedSuccessfully() && sameAsLastSolution(solution)){
57
- return $.Deferred().resolve(lastSubmission.result);
57
+ /**
58
+ * Sends a solution object
59
+ *
60
+ * @param {Submission} submission the submission object
61
+ * @returns {JQuery.Promise<SubmissionResult>}
62
+ */
63
+ _submitSolution(submission) {
64
+ const lastSubmission = mumuki.SubmissionsStore.getSubmissionResultFor(mumuki.exercise.id, submission);
65
+ if (lastSubmission) {
66
+ return $.Deferred().resolve(lastSubmission);
58
67
  } else {
59
- return sendNewSolution(solution);
68
+ return this._sendNewSolution(submission).done((result) => {
69
+ mumuki.SubmissionsStore.setSubmissionResultFor(mumuki.exercise.id, {submission, result});
70
+ });
60
71
  }
61
- },
62
- };
72
+ }
63
73
 
64
- mumuki.bridge = {
65
- Laboratory: Laboratory
66
- };
74
+ /**
75
+ * @param {Submission} submission the submission object
76
+ * @returns {JQuery.Promise<SubmissionResult>}
77
+ */
78
+ _sendNewSolution(submission){
79
+ var token = new mumuki.CsrfToken();
80
+ var request = token.newRequest({
81
+ type: 'POST',
82
+ url: window.location.origin + window.location.pathname + '/solutions' + window.location.search,
83
+ data: submission
84
+ });
85
+ return $.ajax(request).then((result) => this._preRenderResult(result));
86
+ }
67
87
 
68
- }(mumuki));
88
+ /**
89
+ * Pre-renders some html parts of submission UI, adding them to the given result
90
+ *
91
+ * @param {SubmissionResult} result
92
+ * @returns {SubmissionResult}
93
+ */
94
+ _preRenderResult(result) {
95
+ result.class_for_progress_list_item = mumuki.renderers.progressListItemClassForStatus(result.status, true)
96
+ return result;
97
+ }
98
+ }
99
+
100
+ return {
101
+ Laboratory
102
+ };
103
+ })();