Skip to the code
cf.

Chainable calculator

Write calculator(start). It returns a calculator object that holds a running total, starting at start, which defaults to 0. Every calculator has its own total.

The calculator has four methods that each take a number: add(n) adds n to the total, subtract(n) subtracts it, multiply(n) multiplies the total by it, and divide(n) divides the total by it. Each one returns the calculator itself, so calls can be chained, and each works on the total left by the calls before it.

value() returns the current total.

divide(0) throws a RangeError and leaves the total as it was.

calculator(2).add(3).multiply(4).value();           // 20: (2 + 3) * 4
calculator().add(10).subtract(4).divide(2).value(); // 3
 
const calc = calculator(1);
calc.add(2) === calc; // true: every method returns the calculator
calc.multiply(3);
calc.value();         // 9
calc.divide(0);       // throws a RangeError
calc.value();         // 9

What the tests check

  1. value() returns the starting total
    expect(calculator(5).value()).toBe(5);
    expect(calculator(-2.5).value()).toBe(-2.5);
    
  2. starts at 0 by default
    expect(calculator().value()).toBe(0);
    expect(calculator().add(4).value()).toBe(4);
    
  3. applies each step to the total so far, in order
    expect(calculator(2).add(3).multiply(4).value()).toBe(20);
    expect(calculator(2).multiply(4).add(3).value()).toBe(11);
    expect(calculator().add(10).subtract(4).divide(2).value()).toBe(3);
    
  4. every method returns the calculator itself
    const calc = calculator(1);
    expect(calc.add(1)).toBe(calc);
    expect(calc.subtract(1)).toBe(calc);
    expect(calc.multiply(2)).toBe(calc);
    expect(calc.divide(2)).toBe(calc);
    
  5. steps made in separate statements add up
    const calc = calculator(1);
    calc.add(2);
    calc.multiply(3);
    expect(calc.value()).toBe(9);
    calc.subtract(1).divide(4);
    expect(calc.value()).toBe(2);
    
  6. calculators don't share a total
    const a = calculator(1);
    const b = calculator(100);
    a.add(1);
    b.subtract(50);
    expect(a.value()).toBe(2);
    expect(b.value()).toBe(50);
    
  7. divide(0) throws a RangeError and keeps the total
    const calc = calculator(6);
    expect(() => calc.divide(0)).toThrow(RangeError);
    expect(calc.value()).toBe(6);
    expect(calc.divide(2).value()).toBe(3);
    

Learn more

solution.jsSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).