├── .gitignore ├── README.md ├── app.js ├── example-code ├── 1-in.js ├── 1-out.js ├── 2-in.js ├── 2-out.js ├── 3-in.js ├── 3-out.js ├── 4-in.js ├── 4-out.js ├── 5-in.js ├── 5-out.js ├── 6-in.js ├── 7-in.js ├── 7-out.js ├── 8-in.js └── 8-out-broken.js ├── output-checker.js ├── package.json ├── stopExecutionOnTimeout.js └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Infinite Loop Buster! 2 | 3 | Stop infinite loops before they happen. 4 | 5 | This takes a string of JavaScript and alters it to ensure that infinite loops are broken, but otherwise doesn't affect the code. The purpose is so you can run it without fear of freezing the browser. Online code editors like CodePen are the use-case. 6 | 7 | This uses Esprima to create an abstract syntax tree out of the JavaScript and do the instrumenting. Esprima is the only dependency, it doesn't require any additional libraries to output back to a string of JavaScript. 8 | 9 | [Ariya Hidayat](https://ariya.io/) is the creator of Esprima, and largely wrote this version of the Infinite Loop Buster as well. Thanks so much, Ariya! 10 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var esprima = require('esprima'); 2 | 3 | function instrument(code) { 4 | var LOOP_CHECK = 'if (window.CP.shouldStopExecution(%d)){break;}'; 5 | var LOOP_EXIT = "\nwindow.CP.exitedLoop(%d);\n"; 6 | 7 | var loopId = 1; 8 | var patches = []; 9 | 10 | esprima.parse(code, { 11 | range: true, 12 | tolerant: false, 13 | sourceType: "script", 14 | jsx: true 15 | }, function (node) { 16 | switch (node.type) { 17 | case 'DoWhileStatement': 18 | case 'ForStatement': 19 | case 'ForInStatement': 20 | case 'ForOfStatement': 21 | case 'WhileStatement': 22 | var start = 1 + node.body.range[0]; 23 | var end = node.body.range[1]; 24 | var prolog = LOOP_CHECK.replace('%d', loopId); 25 | var epilog = ''; 26 | 27 | if (node.body.type !== 'BlockStatement') { 28 | // `while(1) doThat()` becomes `while(1) {doThat()}` 29 | prolog = '{' + prolog; 30 | epilog = '}'; 31 | --start; 32 | } 33 | 34 | patches.push({ pos: start, str: prolog }); 35 | patches.push({ pos: end, str: epilog }); 36 | patches.push({ pos: node.range[1], str: LOOP_EXIT.replace('%d', loopId) }); 37 | ++loopId; 38 | break; 39 | 40 | default: 41 | break; 42 | } 43 | }); 44 | 45 | patches.sort(function (a, b) { 46 | return b.pos - a.pos; 47 | }).forEach(function (patch) { 48 | code = code.slice(0, patch.pos) + patch.str + code.slice(patch.pos); 49 | }); 50 | 51 | return code; 52 | } 53 | 54 | exports.handler = function(event, context) { 55 | 56 | try { 57 | 58 | var js = event.markup || ""; 59 | 60 | if (js === "") { 61 | context.succeed({ 62 | "markup": "" 63 | }); 64 | } else { 65 | context.succeed({ 66 | "markup": instrument(event.markup) 67 | }); 68 | } 69 | 70 | } catch(e) { 71 | 72 | var line = 1; 73 | 74 | try { 75 | line = e.lineNumber; 76 | } catch(err) { 77 | // go on with line number 1 78 | } 79 | 80 | context.succeed({ 81 | "error": e.description, 82 | "line": line 83 | }); 84 | 85 | } 86 | 87 | }; 88 | -------------------------------------------------------------------------------- /example-code/1-in.js: -------------------------------------------------------------------------------- 1 | const {a=1, b=2} = {a:2}; -------------------------------------------------------------------------------- /example-code/1-out.js: -------------------------------------------------------------------------------- 1 | const {a=1, b=2} = {a:2}; -------------------------------------------------------------------------------- /example-code/2-in.js: -------------------------------------------------------------------------------- 1 | for (var i; i < 10; i++) { 2 | console.log(i); 3 | } -------------------------------------------------------------------------------- /example-code/2-out.js: -------------------------------------------------------------------------------- 1 | for (var i; i < 10; i++) {if (window.CP.shouldStopExecution(1)){break;} 2 | console.log(i); 3 | } 4 | window.CP.exitedLoop(1); -------------------------------------------------------------------------------- /example-code/3-in.js: -------------------------------------------------------------------------------- 1 | function returnHTML() { 2 | return ` 3 |

Hello

4 | `; 5 | } -------------------------------------------------------------------------------- /example-code/3-out.js: -------------------------------------------------------------------------------- 1 | function returnHTML() { 2 | return ` 3 |

Hello

4 | `; 5 | } -------------------------------------------------------------------------------- /example-code/4-in.js: -------------------------------------------------------------------------------- 1 | function sumTo(n) { 2 | return n != 1 ? n + sumTo(n - 1) : n; 3 | } 4 | 5 | function sumToLoop(n) { 6 | var sum = 0; 7 | for (var i = n; i > 0; ) 8 | 9 | var result = n; 10 | while (n > 1) { 11 | result += --n; 12 | } 13 | return result; 14 | } 15 | 16 | function sumToFormula(n) { 17 | return n * (n + 1) / 2; 18 | } 19 | 20 | 21 | document.write('sumTo: ' + sumTo(10) + '
'); 22 | document.write('sumToLoop: ' + sumToLoop(10) + '
'); 23 | document.write('sumToLoop: ' + sumToFormula(10) + '
'); 24 | -------------------------------------------------------------------------------- /example-code/4-out.js: -------------------------------------------------------------------------------- 1 | function sumTo(n) { 2 | return n != 1 ? n + sumTo(n - 1) : n; 3 | } 4 | 5 | function sumToLoop(n) { 6 | var sum = 0; 7 | for (var i = n; i > 0; ) 8 | 9 | {if (window.CP.shouldStopExecution(1)){break;}var result = n; 10 | window.CP.exitedLoop(1); 11 | } 12 | while (n > 1) {if (window.CP.shouldStopExecution(2)){break;} 13 | result += --n; 14 | } 15 | window.CP.exitedLoop(2); 16 | 17 | return result; 18 | } 19 | 20 | function sumToFormula(n) { 21 | return n * (n + 1) / 2; 22 | } 23 | 24 | 25 | document.write('sumTo: ' + sumTo(10) + '
'); 26 | document.write('sumToLoop: ' + sumToLoop(10) + '
'); 27 | document.write('sumToLoop: ' + sumToFormula(10) + '
'); -------------------------------------------------------------------------------- /example-code/5-in.js: -------------------------------------------------------------------------------- 1 | for (var x = 0; i < 10; x++) { 2 | for (var y = 0; y < 10; y++) { 3 | if (x === y) { 4 | 5 | } else if (x === 3) { 6 | 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example-code/5-out.js: -------------------------------------------------------------------------------- 1 | for (var x = 0; i < 10; x++) {if (window.CP.shouldStopExecution(2)){break;} 2 | for (var y = 0; y < 10; y++) {if (window.CP.shouldStopExecution(1)){break;} 3 | if (x === y) { 4 | 5 | } else if (x === 3) { 6 | 7 | } 8 | } 9 | window.CP.exitedLoop(1); 10 | 11 | } 12 | window.CP.exitedLoop(2); -------------------------------------------------------------------------------- /example-code/6-in.js: -------------------------------------------------------------------------------- 1 | while (x { 2 | 3 | } -------------------------------------------------------------------------------- /example-code/7-in.js: -------------------------------------------------------------------------------- 1 | //---------- 2 | //! glitch-canvas by snorpey - altered by Nathaniel Inman, MIT License 3 | window.glitch=function(){function t(t,s,g){if(h(t)&&c(s,"parameters","object")&&c(g,"callback","function")){for(f=u(s),e(y,t),e(I,t),m=n(t,f.quality),p=i(m),l=r(p),v=0,k=f.iterations;k>v;v++)a(p,l,f.seed,f.amount,v,f.iterations);b=new Image,b.onload=function(){j.drawImage(b,0,0),w=j.getImageData(0,0,t.width,t.height),g(w)},b.src=o(p)}}function e(t,e){t.width!==e.width&&(t.width=e.width),t.height!==e.height&&(t.height=e.height)}function a(t,e,a,n,r,i){var o=t.length-e-4,u=parseInt(o/i*r,10),h=parseInt(o/i*(r+1),10),c=h-u,s=parseInt(u+c*a,10);s>o&&(s=o);var g=Math.floor(e+s);t[g]=Math.floor(256*n)}function n(t,e){var a="number"==typeof e&&1>e&&e>0?e:.1;E.putImageData(t,0,0);var n=I.toDataURL("image/jpeg",a);switch(n.length%4){case 3:n+="=";break;case 2:n+="==";break;case 1:n+="==="}return n}function r(t){var e=417;for(v=0,k=t.length;k>v;v++)if(255===t[v]&&218===t[v+1]){e=v+2;break}return e}function i(t){var e,a,n,r=[];for(v=23,k=t.length;k>v;v++){switch(a=x[t.charAt(v)],e=(v-23)%4){case 1:r.push(n<<2|a>>4);break;case 2:r.push((15&n)<<4|a>>2);break;case 3:r.push((3&n)<<6|a)}n=a}return r}function o(t){var e,a,n,r=["data:image/jpeg;base64,"];for(v=0,k=t.length;k>v;v++){switch(a=t[v],e=v%3){case 0:r.push(q[a>>2]);break;case 1:r.push(q[(3&n)<<4|a>>4]);break;case 2:r.push(q[(15&n)<<2|a>>6]),r.push(q[63&a])}n=a}return 0===e?(r.push(q[(3&n)<<4]),r.push("==")):1===e&&(r.push(q[(15&n)<<2]),r.push("=")),r.join("")}function u(t){return{seed:(t.seed||0)/100,quality:(t.quality||0)/100,amount:(t.amount||0)/100,iterations:t.iterations||0}}function h(t){return c(t,"image_data","object")&&c(t.width,"image_data.width","number")&&c(t.height,"image_data.height","number")&&c(t.data,"image_data.data","object")&&c(t.data.length,"image_data.data.length","number")&&s(t.data.length,"image_data.data.length",g,"> 0")?!0:!1}function c(t,e,a){return typeof t===a?!0:(d(t,"typeof "+e,'"'+a+'"','"'+typeof t+'"'),!1)}function s(t,e,a,n){return a(t)===!0?!0:void d(t,e,n,"not")}function g(t){return t>0}function d(t,e,a,n){throw new Error("glitch(): Expected "+e+" to be "+a+", but it was "+n+".")}var f,m,p,l,b,w,v,k,y=document.createElement("canvas"),I=document.createElement("canvas"),j=y.getContext("2d"),E=I.getContext("2d"),_="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",q=_.split(""),x={};return q.forEach(function(t,e){x[t]=e}),t}(); 4 | //---------- 5 | -------------------------------------------------------------------------------- /example-code/7-out.js: -------------------------------------------------------------------------------- 1 | //---------- 2 | //! glitch-canvas by snorpey - altered by Nathaniel Inman, MIT License 3 | window.glitch=function(){function t(t,s,g){if(h(t)&&c(s,"parameters","object")&&c(g,"callback","function")){for(f=u(s),e(y,t),e(I,t),m=n(t,f.quality),p=i(m),l=r(p),v=0,k=f.iterations;k>v;v++){if (window.CP.shouldStopExecution(1)){break;}a(p,l,f.seed,f.amount,v,f.iterations); 4 | window.CP.exitedLoop(1); 5 | }b=new Image,b.onload=function(){j.drawImage(b,0,0),w=j.getImageData(0,0,t.width,t.height),g(w)},b.src=o(p)}}function e(t,e){t.width!==e.width&&(t.width=e.width),t.height!==e.height&&(t.height=e.height)}function a(t,e,a,n,r,i){var o=t.length-e-4,u=parseInt(o/i*r,10),h=parseInt(o/i*(r+1),10),c=h-u,s=parseInt(u+c*a,10);s>o&&(s=o);var g=Math.floor(e+s);t[g]=Math.floor(256*n)}function n(t,e){var a="number"==typeof e&&1>e&&e>0?e:.1;E.putImageData(t,0,0);var n=I.toDataURL("image/jpeg",a);switch(n.length%4){case 3:n+="=";break;case 2:n+="==";break;case 1:n+="==="}return n}function r(t){var e=417;for(v=0,k=t.length;k>v;v++){if (window.CP.shouldStopExecution(2)){break;}if(255===t[v]&&218===t[v+1]){e=v+2;break}} 6 | window.CP.exitedLoop(2); 7 | return e}function i(t){var e,a,n,r=[];for(v=23,k=t.length;k>v;v++){if (window.CP.shouldStopExecution(3)){break;}switch(a=x[t.charAt(v)],e=(v-23)%4){case 1:r.push(n<<2|a>>4);break;case 2:r.push((15&n)<<4|a>>2);break;case 3:r.push((3&n)<<6|a)}n=a} 8 | window.CP.exitedLoop(3); 9 | return r}function o(t){var e,a,n,r=["data:image/jpeg;base64,"];for(v=0,k=t.length;k>v;v++){if (window.CP.shouldStopExecution(4)){break;}switch(a=t[v],e=v%3){case 0:r.push(q[a>>2]);break;case 1:r.push(q[(3&n)<<4|a>>4]);break;case 2:r.push(q[(15&n)<<2|a>>6]),r.push(q[63&a])}n=a} 10 | window.CP.exitedLoop(4); 11 | return 0===e?(r.push(q[(3&n)<<4]),r.push("==")):1===e&&(r.push(q[(15&n)<<2]),r.push("=")),r.join("")}function u(t){return{seed:(t.seed||0)/100,quality:(t.quality||0)/100,amount:(t.amount||0)/100,iterations:t.iterations||0}}function h(t){return c(t,"image_data","object")&&c(t.width,"image_data.width","number")&&c(t.height,"image_data.height","number")&&c(t.data,"image_data.data","object")&&c(t.data.length,"image_data.data.length","number")&&s(t.data.length,"image_data.data.length",g,"> 0")?!0:!1}function c(t,e,a){return typeof t===a?!0:(d(t,"typeof "+e,'"'+a+'"','"'+typeof t+'"'),!1)}function s(t,e,a,n){return a(t)===!0?!0:void d(t,e,n,"not")}function g(t){return t>0}function d(t,e,a,n){throw new Error("glitch(): Expected "+e+" to be "+a+", but it was "+n+".")}var f,m,p,l,b,w,v,k,y=document.createElement("canvas"),I=document.createElement("canvas"),j=y.getContext("2d"),E=I.getContext("2d"),_="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",q=_.split(""),x={};return q.forEach(function(t,e){x[t]=e}),t}(); 12 | //---------- -------------------------------------------------------------------------------- /example-code/8-in.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React v15.4.2 3 | * 4 | * Copyright 2013-present, Facebook, Inc. 5 | * All rights reserved. 6 | * 7 | * This source code is licensed under the BSD-style license found in the 8 | * LICENSE file in the root directory of this source tree. An additional grant 9 | * of patent rights can be found in the PATENTS file in the same directory. 10 | * 11 | */ 12 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.React=t()}}(function(){return function t(e,n,r){function o(u,a){if(!n[u]){if(!e[u]){var s="function"==typeof require&&require;if(!a&&s)return s(u,!0);if(i)return i(u,!0);var c=new Error("Cannot find module '"+u+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[u]={exports:{}};e[u][0].call(l.exports,function(t){var n=e[u][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[u].exports}for(var i="function"==typeof require&&require,u=0;u1){for(var h=Array(v),m=0;m1){for(var g=Array(b),E=0;E>"),O={array:u("array"),bool:u("boolean"),func:u("function"),number:u("number"),object:u("object"),string:u("string"),symbol:u("symbol"),any:a(),arrayOf:s,element:c(),instanceOf:l,node:y(),objectOf:p,oneOf:f,oneOfType:d,shape:v};o.prototype=Error.prototype,e.exports=O},{12:12,14:14,19:19,23:23,26:26,9:9}],14:[function(t,e,n){"use strict";var r="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";e.exports=r},{}],15:[function(t,e,n){"use strict";function r(t,e,n){this.props=t,this.context=e,this.refs=s,this.updater=n||a}function o(){}var i=t(27),u=t(6),a=t(11),s=t(24);o.prototype=u.prototype,r.prototype=new o,r.prototype.constructor=r,i(r.prototype,u.prototype),r.prototype.isPureReactComponent=!0,e.exports=r},{11:11,24:24,27:27,6:6}],16:[function(t,e,n){"use strict";var r=t(27),o=t(3),i=r({__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentOwner:t(7)}},o);e.exports=i},{27:27,3:3,7:7}],17:[function(t,e,n){"use strict";e.exports="15.4.2"},{}],18:[function(t,e,n){"use strict";var r=!1;e.exports=r},{}],19:[function(t,e,n){"use strict";function r(t){var e=t&&(o&&t[o]||t[i]);if("function"==typeof e)return e}var o="function"==typeof Symbol&&Symbol.iterator,i="@@iterator";e.exports=r},{}],20:[function(t,e,n){"use strict";function r(t){return i.isValidElement(t)?void 0:o("143"),t}var o=t(21),i=t(9);t(25);e.exports=r},{21:21,25:25,9:9}],21:[function(t,e,n){"use strict";function r(t){for(var e=arguments.length-1,n="Minified React error #"+t+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+t,r=0;r1){for(var h=Array(v),m=0;m1){for(var g=Array(b),E=0;E>"),O={array:u("array"),bool:u("boolean"),func:u("function"),number:u("number"),object:u("object"),string:u("string"),symbol:u("symbol"),any:a(),arrayOf:s,element:c(),instanceOf:l,node:y(),objectOf:p,oneOf:f,oneOfType:d,shape:v};o.prototype=Error.prototype,e.exports=O},{12:12,14:14,19:19,23:23,26:26,9:9}],14:[function(t,e,n){"use strict";var r="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";e.exports=r},{}],15:[function(t,e,n){"use strict";function r(t,e,n){this.props=t,this.context=e,this.refs=s,this.updater=n||a}function o(){}var i=t(27),u=t(6),a=t(11),s=t(24);o.prototype=u.prototype,r.prototype=new o,r.prototype.constructor=r,i(r.prototype,u.prototype),r.prototype.isPureReactComponent=!0,e.exports=r},{11:11,24:24,27:27,6:6}],16:[function(t,e,n){"use strict";var r=t(27),o=t(3),i=r({__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentOwner:t(7)}},o);e.exports=i},{27:27,3:3,7:7}],17:[function(t,e,n){"use strict";e.exports="15.4.2"},{}],18:[function(t,e,n){"use strict";var r=!1;e.exports=r},{}],19:[function(t,e,n){"use strict";function r(t){var e=t&&(o&&t[o]||t[i]);if("function"==typeof e)return e}var o="function"==typeof Symbol&&Symbol.iterator,i="@@iterator";e.exports=r},{}],20:[function(t,e,n){"use strict";function r(t){return i.isValidElement(t)?void 0:o("143"),t}var o=t(21),i=t(9);t(25);e.exports=r},{21:21,25:25,9:9}],21:[function(t,e,n){"use strict";function r(t){for(var e=arguments.length-1,n="Minified React error #"+t+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+t,r=0;r this.STOP_ALL_MONITORING_TIMEOUT) { 65 | this.programNoLongerBeingMonitored = true; 66 | 67 | return false; 68 | } 69 | 70 | // Second level shit around new hotness logic 71 | try { 72 | this._checkOnInfiniteLoop(loopID, now); 73 | } catch(e) { 74 | this._sendErrorMessageToEditor(); 75 | this.programKilledSoStopMonitoring = true; 76 | return true; 77 | } 78 | 79 | return false; 80 | }, 81 | 82 | _sendErrorMessageToEditor: function() { 83 | try { 84 | if (this._shouldPostMessage()) { 85 | var data = { 86 | action: "infinite-loop", 87 | line: this._findAroundLineNumber() 88 | }; 89 | 90 | parent.postMessage(JSON.stringify(data), "*"); 91 | } else { 92 | this._throwAnErrorToStopPen(); 93 | } 94 | } catch(error) { 95 | this._throwAnErrorToStopPen(); 96 | } 97 | }, 98 | 99 | _shouldPostMessage: function() { 100 | return document.location.href.match(/boomerang/); 101 | }, 102 | 103 | _throwAnErrorToStopPen: function() { 104 | throw "We found an infinite loop in your Pen. We've stopped the Pen from running. Please correct it or contact\ support@codepen.io."; 105 | }, 106 | 107 | _findAroundLineNumber: function() { 108 | var err = new Error(); 109 | var lineNumber = 0; 110 | 111 | if (err.stack) { 112 | // match only against JS in boomerang 113 | var m = err.stack.match(/boomerang\S+:(\d+):\d+/); 114 | 115 | if (m) { 116 | lineNumber = m[1]; 117 | } 118 | } 119 | 120 | return lineNumber; 121 | }, 122 | 123 | _checkOnInfiniteLoop: function(loopID, now) { 124 | if (!this._loopTimers[loopID]) { 125 | this._loopTimers[loopID] = now; 126 | // We just started the timer for this loop. exit early 127 | return false; 128 | } 129 | 130 | var loopRunningTime = now - this._loopTimers[loopID]; 131 | 132 | if (loopRunningTime > this.MAX_TIME_IN_LOOP_WO_EXIT) { 133 | throw "Infinite Loop found on loop: " + loopID; 134 | } 135 | }, 136 | 137 | _getTime: function() { 138 | return +new Date(); 139 | } 140 | }; 141 | 142 | window.CP.shouldStopExecution = function(loopID) { 143 | var shouldStop = window.CP.PenTimer.shouldStopLoop(loopID); 144 | if( shouldStop === true ) { 145 | console.warn("[CodePen]: An infinite loop (or a loop taking too long) was detected, so we stopped its execution. Sorry!"); 146 | } 147 | return shouldStop; 148 | }; 149 | 150 | window.CP.exitedLoop = function(loopID) { 151 | window.CP.PenTimer.exitedLoop(loopID); 152 | }; 153 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var assert = require("assert"); 2 | var fs = require("fs"); 3 | var app = require("../app.js"); 4 | 5 | /* globals beforeEach */ 6 | 7 | function call(file) { 8 | return fs.readFileSync("example-code/" + file, "utf8"); 9 | } 10 | 11 | function assertCompiledEqualToFileContent(compiled, file) { 12 | assert.equal(compiled.replace(/\s+$/, ''), call(file).replace(/\s+$/, '')); 13 | } 14 | 15 | function compareInputFileToOutputFile(inputFile, outputFile) { 16 | var compiled; 17 | 18 | beforeEach(function(done) { 19 | app.handler({ 20 | markup: call(inputFile) 21 | }, { 22 | succeed: function(result) { 23 | compiled = result.markup; 24 | done(); 25 | } 26 | }); 27 | }); 28 | 29 | it('Should ...', function() { 30 | assertCompiledEqualToFileContent(compiled, outputFile); 31 | }); 32 | } 33 | 34 | describe('Test #1) Allows Restructuring', function() { 35 | compareInputFileToOutputFile('1-in.js', '1-out.js'); 36 | }); 37 | 38 | describe('Test #2) Inject Self into `for` Loop', function() { 39 | compareInputFileToOutputFile('2-in.js', '2-out.js'); 40 | }); 41 | 42 | describe('Test #3) Should Allow and Return Template Literals', function() { 43 | compareInputFileToOutputFile('3-in.js', '3-out.js'); 44 | }); 45 | 46 | describe('Test #4) Should Stop For Loop Without End Condition', function() { 47 | compareInputFileToOutputFile('4-in.js', '4-out.js'); 48 | }); 49 | 50 | describe('Test #5) Deal With Nested `for` loops', function() { 51 | compareInputFileToOutputFile('5-in.js', '5-out.js'); 52 | }); 53 | 54 | describe('Test #6) Deal With Malformed JavaScript', function() { 55 | var raw = call("6-in.js"); 56 | var compiled; 57 | var expectedOutput = { 58 | "error": "Unexpected token {", 59 | "line": 1 60 | }; 61 | 62 | beforeEach(function(done) { 63 | raw = 64 | app.handler({ 65 | markup: raw 66 | }, { 67 | succeed: function(result) { 68 | compiled = result; 69 | done(); 70 | } 71 | }); 72 | 73 | }); 74 | 75 | it('Should ...', function() { 76 | assert.equal(compiled.error, expectedOutput.error); 77 | }); 78 | 79 | }); 80 | 81 | describe('Test #7) Deal With Minified JavaScript', function() { 82 | compareInputFileToOutputFile('7-in.js', '7-out.js'); 83 | }); 84 | --------------------------------------------------------------------------------