mockContext.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. (function() {
  2. // Code from http://stackoverflow.com/questions/4406864/html-canvas-unit-testing
  3. var Context = function() {
  4. this._calls = []; // names/args of recorded calls
  5. this._initMethods();
  6. this._fillStyle = null;
  7. this._lineCap = null;
  8. this._lineDashOffset = null;
  9. this._lineJoin = null;
  10. this._lineWidth = null;
  11. this._strokeStyle = null;
  12. // Define properties here so that we can record each time they are set
  13. Object.defineProperties(this, {
  14. "fillStyle": {
  15. 'get': function() { return this._fillStyle; },
  16. 'set': function(style) {
  17. this._fillStyle = style;
  18. this.record('setFillStyle', [style]);
  19. }
  20. },
  21. 'lineCap': {
  22. 'get': function() { return this._lineCap; },
  23. 'set': function(cap) {
  24. this._lineCap = cap;
  25. this.record('setLineCap', [cap]);
  26. }
  27. },
  28. 'lineDashOffset': {
  29. 'get': function() { return this._lineDashOffset; },
  30. 'set': function(offset) {
  31. this._lineDashOffset = offset;
  32. this.record('setLineDashOffset', [offset]);
  33. }
  34. },
  35. 'lineJoin': {
  36. 'get': function() { return this._lineJoin; },
  37. 'set': function(join) {
  38. this._lineJoin = join;
  39. this.record('setLineJoin', [join]);
  40. }
  41. },
  42. 'lineWidth': {
  43. 'get': function() { return this._lineWidth; },
  44. 'set': function (width) {
  45. this._lineWidth = width;
  46. this.record('setLineWidth', [width]);
  47. }
  48. },
  49. 'strokeStyle': {
  50. 'get': function() { return this._strokeStyle; },
  51. 'set': function(style) {
  52. this._strokeStyle = style;
  53. this.record('setStrokeStyle', [style]);
  54. }
  55. },
  56. });
  57. };
  58. Context.prototype._initMethods = function() {
  59. // define methods to test here
  60. // no way to introspect so we have to do some extra work :(
  61. var methods = {
  62. arc: function() {},
  63. beginPath: function() {},
  64. bezierCurveTo: function() {},
  65. clearRect: function() {},
  66. closePath: function() {},
  67. fill: function() {},
  68. fillRect: function() {},
  69. fillText: function() {},
  70. lineTo: function(x, y) {},
  71. measureText: function(text) {
  72. // return the number of characters * fixed size
  73. return text ? { width: text.length * 10 } : {width: 0};
  74. },
  75. moveTo: function(x, y) {},
  76. quadraticCurveTo: function() {},
  77. restore: function() {},
  78. rotate: function() {},
  79. save: function() {},
  80. setLineDash: function() {},
  81. stroke: function() {},
  82. strokeRect: function(x, y, w, h) {},
  83. setTransform: function(a, b, c, d, e, f) {},
  84. translate: function(x, y) {},
  85. };
  86. // attach methods to the class itself
  87. var scope = this;
  88. var addMethod = function(name, method) {
  89. scope[methodName] = function() {
  90. scope.record(name, arguments);
  91. return method.apply(scope, arguments);
  92. };
  93. }
  94. for (var methodName in methods) {
  95. var method = methods[methodName];
  96. addMethod(methodName, method);
  97. }
  98. };
  99. Context.prototype.record = function(methodName, args) {
  100. this._calls.push({
  101. name: methodName,
  102. args: Array.prototype.slice.call(args)
  103. });
  104. },
  105. Context.prototype.getCalls = function() {
  106. return this._calls;
  107. }
  108. Context.prototype.resetCalls = function() {
  109. this._calls = [];
  110. };
  111. window.createMockContext = function() {
  112. return new Context();
  113. };
  114. // Custom matcher
  115. function toBeCloseToPixel() {
  116. return {
  117. compare: function(actual, expected) {
  118. var result = false;
  119. if (!isNaN(actual) && !isNaN(expected)) {
  120. var diff = Math.abs(actual - expected);
  121. var A = Math.abs(actual);
  122. var B = Math.abs(expected);
  123. var percentDiff = 0.005; // 0.5% diff
  124. result = (diff <= (A > B ? A : B) * percentDiff) || diff < 2; // 2 pixels is fine
  125. }
  126. return { pass: result };
  127. }
  128. }
  129. };
  130. function toEqualOneOf() {
  131. return {
  132. compare: function(actual, expecteds) {
  133. var result = false;
  134. for (var i = 0, l = expecteds.length; i < l; i++) {
  135. if (actual === expecteds[i]) {
  136. result = true;
  137. break;
  138. }
  139. }
  140. return {
  141. pass: result
  142. };
  143. }
  144. };
  145. }
  146. window.addDefaultMatchers = function(jasmine) {
  147. jasmine.addMatchers({
  148. toBeCloseToPixel: toBeCloseToPixel,
  149. toEqualOneOf: toEqualOneOf
  150. });
  151. }
  152. // Canvas injection helpers
  153. var charts = {};
  154. function acquireChart(config, style) {
  155. var wrapper = document.createElement("div");
  156. var canvas = document.createElement("canvas");
  157. wrapper.className = 'chartjs-wrapper';
  158. style = style || { height: '512px', width: '512px' };
  159. for (var k in style) {
  160. wrapper.style[k] = style[k];
  161. canvas.style[k] = style[k];
  162. }
  163. canvas.height = canvas.style.height && parseInt(canvas.style.height);
  164. canvas.width = canvas.style.width && parseInt(canvas.style.width);
  165. // by default, remove chart animation and auto resize
  166. var options = config.options = config.options || {};
  167. options.animation = options.animation === undefined? false : options.animation;
  168. options.responsive = options.responsive === undefined? false : options.responsive;
  169. options.defaultFontFamily = options.defaultFontFamily || 'Arial';
  170. wrapper.appendChild(canvas);
  171. window.document.body.appendChild(wrapper);
  172. var chart = new Chart(canvas.getContext("2d"), config);
  173. charts[chart.id] = chart;
  174. return chart;
  175. }
  176. function releaseChart(chart) {
  177. chart.chart.canvas.parentNode.remove();
  178. delete charts[chart.id];
  179. delete chart;
  180. }
  181. function releaseAllCharts(scope) {
  182. for (var id in charts) {
  183. var chart = charts[id];
  184. releaseChart(chart);
  185. }
  186. }
  187. function injectCSS(css) {
  188. // http://stackoverflow.com/q/3922139
  189. var head = document.getElementsByTagName('head')[0];
  190. var style = document.createElement('style');
  191. style.setAttribute('type', 'text/css');
  192. if (style.styleSheet) { // IE
  193. style.styleSheet.cssText = css;
  194. } else {
  195. style.appendChild(document.createTextNode(css));
  196. }
  197. head.appendChild(style);
  198. }
  199. window.acquireChart = acquireChart;
  200. window.releaseChart = releaseChart;
  201. window.releaseAllCharts = releaseAllCharts;
  202. // some style initialization to limit differences between browsers across different plateforms.
  203. injectCSS(
  204. '.chartjs-wrapper, .chartjs-wrapper canvas {' +
  205. 'border: 0;' +
  206. 'margin: 0;' +
  207. 'padding: 0;' +
  208. '}' +
  209. '.chartjs-wrapper {' +
  210. 'position: absolute' +
  211. '}');
  212. })();