src/data-canvas.js
function RecordingContext(ctx) {
  forward(this, ctx, true /* only foward accessors */);

  var calls = [];
  this.calls = calls;

  for (var k in ctx) {
    (function(k) {
      if (typeof(ctx[k]) != 'function') return;
      this[k] = function() {
        // TODO: record current drawing style
        var args = Array.prototype.slice.call(arguments);
        calls.push([k].concat(args));
        return ctx[k].apply(ctx, arguments);
      };
    }).bind(this)(k);
  }

  this.pushObject = function(o) {
    calls.push(['pushObject', o]);
  };
  
  this.popObject = function() {
    calls.push(['popObject']);
  };

  this.reset = function() {
    this.calls = calls = [];
  };

  var recordingDrawImage = this.drawImage;  // plain recording drawImage()
  this.drawImage = function(image) {
    // If the drawn image has recorded calls, then they need to be transferred over.
    var recorder = RecordingContext.recorderForCanvas(image);
    if (!recorder) {
      recordingDrawImage.apply(ctx, arguments);
    } else {
      ctx.drawImage.apply(ctx, arguments);
      calls = calls.concat(transformedCalls(recorder.calls, arguments));
      this.calls = calls;
    }
  }
}
function RecordingContext(ctx) {
  forward(this, ctx, true /* only foward accessors */);

  var calls = [];
  this.calls = calls;

  for (var k in ctx) {
    (function(k) {
      if (typeof(ctx[k]) != 'function') return;
      this[k] = function() {
        // TODO: record current drawing style
        var args = Array.prototype.slice.call(arguments);
        calls.push([k].concat(args));
        return ctx[k].apply(ctx, arguments);
      };
    }).bind(this)(k);
  }

  this.pushObject = function(o) {
    calls.push(['pushObject', o]);
  };
  
  this.popObject = function() {
    calls.push(['popObject']);
  };

  this.reset = function() {
    this.calls = calls = [];
  };

  var recordingDrawImage = this.drawImage;  // plain recording drawImage()
  this.drawImage = function(image) {
    // If the drawn image has recorded calls, then they need to be transferred over.
    var recorder = RecordingContext.recorderForCanvas(image);
    if (!recorder) {
      recordingDrawImage.apply(ctx, arguments);
    } else {
      ctx.drawImage.apply(ctx, arguments);
      calls = calls.concat(transformedCalls(recorder.calls, arguments));
      this.calls = calls;
    }
  }
}
(CanvasRenderingContext2D, DataContext) pairs 
A context which records what it does (for testing).

This proxies all calls to the underlying canvas, so they do produce visible
drawing. Use `drawnObjectsWith` or `calls` to test what was drawn.