@khanacademy/perseus-score 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -480,48 +480,63 @@ const KhanAnswerTypes = {
480
480
  guess: guess
481
481
  };
482
482
 
483
- // iterate over all the acceptable forms, and if one of the
484
- // answers is correct, return true
485
- acceptableForms.forEach(form => {
486
- const transformed = forms[form](guess);
487
- for (let j = 0, l = transformed.length; j < l; j++) {
488
- const val = transformed[j].value;
489
- const exact = transformed[j].exact;
490
- const piApprox = transformed[j].piApprox;
491
- // If a string was returned, and it exactly matches,
492
- // return true
493
- if (predicate(val, options.maxError)) {
494
- // If the exact correct number was returned,
483
+ // Iterate over all the acceptable forms
484
+ // and exit if one of the answers is correct.
485
+ //
486
+ // HACK: This function is a bug fix from LEMS-2962;
487
+ // after a transition from jQuery's `each` to JS's `forEach`
488
+ // we realized this code was banking on the ability to:
489
+ // 1. exit early from nested loops (can be tricky outside of functions)
490
+ // 2. mutate external variables (score)
491
+ // Could probably be refactored to be a pure function that
492
+ // returns a score, but this code is poorly tested and prone to break.
493
+ const findCorrectAnswer = () => {
494
+ // WARNING: Don't use `forEach` without additional refactoring
495
+ // because code needs to be able to exit early
496
+ for (const form of acceptableForms) {
497
+ const transformed = forms[form](guess);
498
+ for (let j = 0, l = transformed.length; j < l; j++) {
499
+ const val = transformed[j].value;
500
+ const exact = transformed[j].exact;
501
+ const piApprox = transformed[j].piApprox;
502
+ // If a string was returned, and it exactly matches,
495
503
  // return true
496
- if (exact || options.simplify === "optional") {
497
- score.correct = true;
498
- score.message = options.message || null;
499
- // If the answer is correct, don't say it's
500
- // empty. This happens, for example, with the
501
- // coefficient type where guess === "" but is
502
- // interpreted as "1" which is correct.
503
- score.empty = false;
504
- } else if (form === "percent") {
505
- // Otherwise, an error was returned
506
- score.empty = true;
507
- score.message = ErrorCodes.MISSING_PERCENT_ERROR;
508
- } else {
509
- if (options.simplify !== "enforced") {
504
+ if (predicate(val, options.maxError)) {
505
+ // If the exact correct number was returned,
506
+ // return true
507
+ if (exact || options.simplify === "optional") {
508
+ score.correct = true;
509
+ score.message = options.message || null;
510
+ // If the answer is correct, don't say it's
511
+ // empty. This happens, for example, with the
512
+ // coefficient type where guess === "" but is
513
+ // interpreted as "1" which is correct.
514
+ score.empty = false;
515
+ } else if (form === "percent") {
516
+ // Otherwise, an error was returned
510
517
  score.empty = true;
518
+ score.message = ErrorCodes.MISSING_PERCENT_ERROR;
519
+ } else {
520
+ if (options.simplify !== "enforced") {
521
+ score.empty = true;
522
+ }
523
+ score.message = ErrorCodes.NEEDS_TO_BE_SIMPLIFIED_ERROR;
511
524
  }
512
- score.message = ErrorCodes.NEEDS_TO_BE_SIMPLIFIED_ERROR;
525
+ // HACK: The return false below stops the looping of the
526
+ // callback since predicate check succeeded.
527
+ // No more forms to look to verify the user guess.
528
+ return false;
529
+ }
530
+ if (piApprox && predicate(val, Math.abs(val * 0.001))) {
531
+ score.empty = true;
532
+ score.message = ErrorCodes.APPROXIMATED_PI_ERROR;
513
533
  }
514
- // The return false below stops the looping of the
515
- // callback since predicate check succeeded.
516
- // No more forms to look to verify the user guess.
517
- return false;
518
- }
519
- if (piApprox && predicate(val, Math.abs(val * 0.001))) {
520
- score.empty = true;
521
- score.message = ErrorCodes.APPROXIMATED_PI_ERROR;
522
534
  }
523
535
  }
524
- });
536
+ };
537
+
538
+ // mutates `score`
539
+ findCorrectAnswer();
525
540
  if (score.correct === false) {
526
541
  let interpretedGuess = false;
527
542
  _.each(forms, function (form) {