Forráskód Böngészése

Merge branch 'tests' of https://github.com/tlwr/CyberChef into tlwr-tests

n1474335 8 éve
szülő
commit
6e5ea5d75f

+ 50 - 5
Gruntfile.js

@@ -9,10 +9,14 @@ module.exports = function(grunt) {
         "A persistent task which creates a development build whenever source files are modified.",
         ["clean:dev", "concat:css", "concat:js", "copy:htmlDev", "copy:staticDev", "chmod:build", "watch"]);
 
+    grunt.registerTask("test",
+        "A task which runs all the tests in test/tests.",
+        ["clean:test", "concat:jsTest", "copy:htmlTest", "chmod:build", "exec:tests"]);
+
     grunt.registerTask("prod",
         "Creates a production-ready build. Use the --msg flag to add a compile message.",
         ["eslint", "exec:stats", "clean", "jsdoc", "concat", "copy:htmlDev", "copy:htmlProd", "copy:htmlInline",
-         "copy:staticDev", "copy:staticProd", "cssmin", "uglify:prod", "inline", "htmlmin", "chmod"]);
+         "copy:staticDev", "copy:staticProd", "cssmin", "uglify:prod", "inline", "htmlmin", "chmod", "test"]);
 
     grunt.registerTask("docs",
         "Compiles documentation in the /docs directory.",
@@ -50,7 +54,7 @@ module.exports = function(grunt) {
 
 
     // JS includes
-    var jsFiles = [
+    var jsIncludes = [
         // Third party framework libraries
         "src/js/lib/jquery-2.1.1.js",
         "src/js/lib/bootstrap-3.3.6.js",
@@ -134,6 +138,7 @@ module.exports = function(grunt) {
         "src/js/lib/vkbeautify.js",
         "src/js/lib/Sortable.js",
         "src/js/lib/bootstrap-colorpicker.js",
+        "src/js/lib/es6-promise.auto.js",
         "src/js/lib/xpath.js",
 
         // Custom libraries
@@ -154,10 +159,25 @@ module.exports = function(grunt) {
         "src/js/views/html/*.js",
         "!src/js/views/html/main.js",
 
-        // Start the app!
-        "src/js/views/html/main.js",
     ];
 
+    var jsAppFiles = [].concat(
+        jsIncludes,
+        [
+            // Start the main app!
+            "src/js/views/html/main.js",
+        ]
+    );
+
+    var jsTestFiles = [].concat(
+        jsIncludes,
+        [
+            "test/TestRegister.js",
+            "test/tests/**/*.js",
+            "test/TestRunner.js",
+        ]
+    );
+
     var banner = '/**\n\
  * CyberChef - The Cyber Swiss Army Knife\n\
  *\n\
@@ -198,6 +218,11 @@ module.exports = function(grunt) {
             config: ["src/js/config/**/*.js"],
             views: ["src/js/views/**/*.js"],
             operations: ["src/js/operations/**/*.js"],
+            tests: [
+                "test/**/*.js",
+                "!test/PhantomRunner.js",
+                "!test/NodeRunner.js",
+            ],
         },
         jsdoc: {
             options: {
@@ -216,6 +241,7 @@ module.exports = function(grunt) {
         },
         clean: {
             dev: ["build/dev/*"],
+            test: ["build/test/*"],
             prod: ["build/prod/*"],
             docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico"],
         },
@@ -243,8 +269,15 @@ module.exports = function(grunt) {
                 options: {
                     banner: '"use strict";\n'
                 },
-                src: jsFiles,
+                src: jsAppFiles,
                 dest: "build/dev/scripts.js"
+            },
+            jsTest: {
+                options: {
+                    banner: '"use strict";\n'
+                },
+                src: jsTestFiles,
+                dest: "build/test/tests.js"
             }
         },
         copy: {
@@ -257,6 +290,15 @@ module.exports = function(grunt) {
                 src: "src/html/index.html",
                 dest: "build/dev/index.html"
             },
+            htmlTest: {
+                options: {
+                    process: function(content, srcpath) {
+                        return grunt.template.process(content, templateOptions);
+                    }
+                },
+                src: "src/html/test.html",
+                dest: "build/test/index.html"
+            },
             htmlProd: {
                 options: {
                     process: function(content, srcpath) {
@@ -409,6 +451,9 @@ module.exports = function(grunt) {
             }
         },
         exec: {
+            tests: {
+                command: "node test/NodeRunner.js",
+            },
             repoSize: {
                 command: [
                     "git ls-files | wc -l | xargs printf '\n%b\ttracked files\n'",

+ 2 - 1
package.json

@@ -39,6 +39,7 @@
     "grunt-exec": "~1.0.1",
     "grunt-inline-alt": "~0.3.10",
     "grunt-jsdoc": "^2.1.0",
-    "ink-docstrap": "^1.1.4"
+    "ink-docstrap": "^1.1.4",
+    "phantomjs-prebuilt": "^2.1.14"
   }
 }

+ 34 - 0
src/html/test.html

@@ -0,0 +1,34 @@
+<!-- htmlmin:ignore --><!--
+    CyberChef - The Cyber Swiss Army Knife
+    
+    @author tlwr [toby@toby.codes]
+
+    @copyright Crown Copyright 2017
+    @license Apache-2.0
+    
+      Copyright 2017 Crown Copyright
+    
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+    
+        http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<!-- htmlmin:ignore -->
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8">
+        <title>CyberChef</title>
+    </head>
+    <body>
+        <main style="white-space: pre"></main>
+        <script type="application/javascript" src="tests.js"></script>
+    </body>
+</html>

+ 6 - 2
src/js/.eslintrc.json

@@ -109,6 +109,10 @@
         "OutputWaiter": false,
         "RecipeWaiter": false,
         "SeasonalWaiter": false,
-        "WindowWaiter": false
+        "WindowWaiter": false,
+
+        /* tests */
+        "TestRegister": false,
+        "TestRunner": false
     }
-}
+}

+ 1159 - 0
src/js/lib/es6-promise.auto.js

@@ -0,0 +1,1159 @@
+/*!
+ * @overview es6-promise - a tiny implementation of Promises/A+.
+ * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
+ * @license   Licensed under MIT license
+ *            See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
+ * @version   4.0.5
+ */
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global.ES6Promise = factory());
+}(this, (function () { 'use strict';
+
+function objectOrFunction(x) {
+  return typeof x === 'function' || typeof x === 'object' && x !== null;
+}
+
+function isFunction(x) {
+  return typeof x === 'function';
+}
+
+var _isArray = undefined;
+if (!Array.isArray) {
+  _isArray = function (x) {
+    return Object.prototype.toString.call(x) === '[object Array]';
+  };
+} else {
+  _isArray = Array.isArray;
+}
+
+var isArray = _isArray;
+
+var len = 0;
+var vertxNext = undefined;
+var customSchedulerFn = undefined;
+
+var asap = function asap(callback, arg) {
+  queue[len] = callback;
+  queue[len + 1] = arg;
+  len += 2;
+  if (len === 2) {
+    // If len is 2, that means that we need to schedule an async flush.
+    // If additional callbacks are queued before the queue is flushed, they
+    // will be processed by this flush that we are scheduling.
+    if (customSchedulerFn) {
+      customSchedulerFn(flush);
+    } else {
+      scheduleFlush();
+    }
+  }
+};
+
+function setScheduler(scheduleFn) {
+  customSchedulerFn = scheduleFn;
+}
+
+function setAsap(asapFn) {
+  asap = asapFn;
+}
+
+var browserWindow = typeof window !== 'undefined' ? window : undefined;
+var browserGlobal = browserWindow || {};
+var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
+var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && ({}).toString.call(process) === '[object process]';
+
+// test for web worker but not in IE10
+var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
+
+// node
+function useNextTick() {
+  // node version 0.10.x displays a deprecation warning when nextTick is used recursively
+  // see https://github.com/cujojs/when/issues/410 for details
+  return function () {
+    return process.nextTick(flush);
+  };
+}
+
+// vertx
+function useVertxTimer() {
+  if (typeof vertxNext !== 'undefined') {
+    return function () {
+      vertxNext(flush);
+    };
+  }
+
+  return useSetTimeout();
+}
+
+function useMutationObserver() {
+  var iterations = 0;
+  var observer = new BrowserMutationObserver(flush);
+  var node = document.createTextNode('');
+  observer.observe(node, { characterData: true });
+
+  return function () {
+    node.data = iterations = ++iterations % 2;
+  };
+}
+
+// web worker
+function useMessageChannel() {
+  var channel = new MessageChannel();
+  channel.port1.onmessage = flush;
+  return function () {
+    return channel.port2.postMessage(0);
+  };
+}
+
+function useSetTimeout() {
+  // Store setTimeout reference so es6-promise will be unaffected by
+  // other code modifying setTimeout (like sinon.useFakeTimers())
+  var globalSetTimeout = setTimeout;
+  return function () {
+    return globalSetTimeout(flush, 1);
+  };
+}
+
+var queue = new Array(1000);
+function flush() {
+  for (var i = 0; i < len; i += 2) {
+    var callback = queue[i];
+    var arg = queue[i + 1];
+
+    callback(arg);
+
+    queue[i] = undefined;
+    queue[i + 1] = undefined;
+  }
+
+  len = 0;
+}
+
+function attemptVertx() {
+  try {
+    var r = require;
+    var vertx = r('vertx');
+    vertxNext = vertx.runOnLoop || vertx.runOnContext;
+    return useVertxTimer();
+  } catch (e) {
+    return useSetTimeout();
+  }
+}
+
+var scheduleFlush = undefined;
+// Decide what async method to use to triggering processing of queued callbacks:
+if (isNode) {
+  scheduleFlush = useNextTick();
+} else if (BrowserMutationObserver) {
+  scheduleFlush = useMutationObserver();
+} else if (isWorker) {
+  scheduleFlush = useMessageChannel();
+} else if (browserWindow === undefined && typeof require === 'function') {
+  scheduleFlush = attemptVertx();
+} else {
+  scheduleFlush = useSetTimeout();
+}
+
+function then(onFulfillment, onRejection) {
+  var _arguments = arguments;
+
+  var parent = this;
+
+  var child = new this.constructor(noop);
+
+  if (child[PROMISE_ID] === undefined) {
+    makePromise(child);
+  }
+
+  var _state = parent._state;
+
+  if (_state) {
+    (function () {
+      var callback = _arguments[_state - 1];
+      asap(function () {
+        return invokeCallback(_state, child, callback, parent._result);
+      });
+    })();
+  } else {
+    subscribe(parent, child, onFulfillment, onRejection);
+  }
+
+  return child;
+}
+
+/**
+  `Promise.resolve` returns a promise that will become resolved with the
+  passed `value`. It is shorthand for the following:
+
+  ```javascript
+  let promise = new Promise(function(resolve, reject){
+    resolve(1);
+  });
+
+  promise.then(function(value){
+    // value === 1
+  });
+  ```
+
+  Instead of writing the above, your code now simply becomes the following:
+
+  ```javascript
+  let promise = Promise.resolve(1);
+
+  promise.then(function(value){
+    // value === 1
+  });
+  ```
+
+  @method resolve
+  @static
+  @param {Any} value value that the returned promise will be resolved with
+  Useful for tooling.
+  @return {Promise} a promise that will become fulfilled with the given
+  `value`
+*/
+function resolve(object) {
+  /*jshint validthis:true */
+  var Constructor = this;
+
+  if (object && typeof object === 'object' && object.constructor === Constructor) {
+    return object;
+  }
+
+  var promise = new Constructor(noop);
+  _resolve(promise, object);
+  return promise;
+}
+
+var PROMISE_ID = Math.random().toString(36).substring(16);
+
+function noop() {}
+
+var PENDING = void 0;
+var FULFILLED = 1;
+var REJECTED = 2;
+
+var GET_THEN_ERROR = new ErrorObject();
+
+function selfFulfillment() {
+  return new TypeError("You cannot resolve a promise with itself");
+}
+
+function cannotReturnOwn() {
+  return new TypeError('A promises callback cannot return that same promise.');
+}
+
+function getThen(promise) {
+  try {
+    return promise.then;
+  } catch (error) {
+    GET_THEN_ERROR.error = error;
+    return GET_THEN_ERROR;
+  }
+}
+
+function tryThen(then, value, fulfillmentHandler, rejectionHandler) {
+  try {
+    then.call(value, fulfillmentHandler, rejectionHandler);
+  } catch (e) {
+    return e;
+  }
+}
+
+function handleForeignThenable(promise, thenable, then) {
+  asap(function (promise) {
+    var sealed = false;
+    var error = tryThen(then, thenable, function (value) {
+      if (sealed) {
+        return;
+      }
+      sealed = true;
+      if (thenable !== value) {
+        _resolve(promise, value);
+      } else {
+        fulfill(promise, value);
+      }
+    }, function (reason) {
+      if (sealed) {
+        return;
+      }
+      sealed = true;
+
+      _reject(promise, reason);
+    }, 'Settle: ' + (promise._label || ' unknown promise'));
+
+    if (!sealed && error) {
+      sealed = true;
+      _reject(promise, error);
+    }
+  }, promise);
+}
+
+function handleOwnThenable(promise, thenable) {
+  if (thenable._state === FULFILLED) {
+    fulfill(promise, thenable._result);
+  } else if (thenable._state === REJECTED) {
+    _reject(promise, thenable._result);
+  } else {
+    subscribe(thenable, undefined, function (value) {
+      return _resolve(promise, value);
+    }, function (reason) {
+      return _reject(promise, reason);
+    });
+  }
+}
+
+function handleMaybeThenable(promise, maybeThenable, then$$) {
+  if (maybeThenable.constructor === promise.constructor && then$$ === then && maybeThenable.constructor.resolve === resolve) {
+    handleOwnThenable(promise, maybeThenable);
+  } else {
+    if (then$$ === GET_THEN_ERROR) {
+      _reject(promise, GET_THEN_ERROR.error);
+    } else if (then$$ === undefined) {
+      fulfill(promise, maybeThenable);
+    } else if (isFunction(then$$)) {
+      handleForeignThenable(promise, maybeThenable, then$$);
+    } else {
+      fulfill(promise, maybeThenable);
+    }
+  }
+}
+
+function _resolve(promise, value) {
+  if (promise === value) {
+    _reject(promise, selfFulfillment());
+  } else if (objectOrFunction(value)) {
+    handleMaybeThenable(promise, value, getThen(value));
+  } else {
+    fulfill(promise, value);
+  }
+}
+
+function publishRejection(promise) {
+  if (promise._onerror) {
+    promise._onerror(promise._result);
+  }
+
+  publish(promise);
+}
+
+function fulfill(promise, value) {
+  if (promise._state !== PENDING) {
+    return;
+  }
+
+  promise._result = value;
+  promise._state = FULFILLED;
+
+  if (promise._subscribers.length !== 0) {
+    asap(publish, promise);
+  }
+}
+
+function _reject(promise, reason) {
+  if (promise._state !== PENDING) {
+    return;
+  }
+  promise._state = REJECTED;
+  promise._result = reason;
+
+  asap(publishRejection, promise);
+}
+
+function subscribe(parent, child, onFulfillment, onRejection) {
+  var _subscribers = parent._subscribers;
+  var length = _subscribers.length;
+
+  parent._onerror = null;
+
+  _subscribers[length] = child;
+  _subscribers[length + FULFILLED] = onFulfillment;
+  _subscribers[length + REJECTED] = onRejection;
+
+  if (length === 0 && parent._state) {
+    asap(publish, parent);
+  }
+}
+
+function publish(promise) {
+  var subscribers = promise._subscribers;
+  var settled = promise._state;
+
+  if (subscribers.length === 0) {
+    return;
+  }
+
+  var child = undefined,
+      callback = undefined,
+      detail = promise._result;
+
+  for (var i = 0; i < subscribers.length; i += 3) {
+    child = subscribers[i];
+    callback = subscribers[i + settled];
+
+    if (child) {
+      invokeCallback(settled, child, callback, detail);
+    } else {
+      callback(detail);
+    }
+  }
+
+  promise._subscribers.length = 0;
+}
+
+function ErrorObject() {
+  this.error = null;
+}
+
+var TRY_CATCH_ERROR = new ErrorObject();
+
+function tryCatch(callback, detail) {
+  try {
+    return callback(detail);
+  } catch (e) {
+    TRY_CATCH_ERROR.error = e;
+    return TRY_CATCH_ERROR;
+  }
+}
+
+function invokeCallback(settled, promise, callback, detail) {
+  var hasCallback = isFunction(callback),
+      value = undefined,
+      error = undefined,
+      succeeded = undefined,
+      failed = undefined;
+
+  if (hasCallback) {
+    value = tryCatch(callback, detail);
+
+    if (value === TRY_CATCH_ERROR) {
+      failed = true;
+      error = value.error;
+      value = null;
+    } else {
+      succeeded = true;
+    }
+
+    if (promise === value) {
+      _reject(promise, cannotReturnOwn());
+      return;
+    }
+  } else {
+    value = detail;
+    succeeded = true;
+  }
+
+  if (promise._state !== PENDING) {
+    // noop
+  } else if (hasCallback && succeeded) {
+      _resolve(promise, value);
+    } else if (failed) {
+      _reject(promise, error);
+    } else if (settled === FULFILLED) {
+      fulfill(promise, value);
+    } else if (settled === REJECTED) {
+      _reject(promise, value);
+    }
+}
+
+function initializePromise(promise, resolver) {
+  try {
+    resolver(function resolvePromise(value) {
+      _resolve(promise, value);
+    }, function rejectPromise(reason) {
+      _reject(promise, reason);
+    });
+  } catch (e) {
+    _reject(promise, e);
+  }
+}
+
+var id = 0;
+function nextId() {
+  return id++;
+}
+
+function makePromise(promise) {
+  promise[PROMISE_ID] = id++;
+  promise._state = undefined;
+  promise._result = undefined;
+  promise._subscribers = [];
+}
+
+function Enumerator(Constructor, input) {
+  this._instanceConstructor = Constructor;
+  this.promise = new Constructor(noop);
+
+  if (!this.promise[PROMISE_ID]) {
+    makePromise(this.promise);
+  }
+
+  if (isArray(input)) {
+    this._input = input;
+    this.length = input.length;
+    this._remaining = input.length;
+
+    this._result = new Array(this.length);
+
+    if (this.length === 0) {
+      fulfill(this.promise, this._result);
+    } else {
+      this.length = this.length || 0;
+      this._enumerate();
+      if (this._remaining === 0) {
+        fulfill(this.promise, this._result);
+      }
+    }
+  } else {
+    _reject(this.promise, validationError());
+  }
+}
+
+function validationError() {
+  return new Error('Array Methods must be provided an Array');
+};
+
+Enumerator.prototype._enumerate = function () {
+  var length = this.length;
+  var _input = this._input;
+
+  for (var i = 0; this._state === PENDING && i < length; i++) {
+    this._eachEntry(_input[i], i);
+  }
+};
+
+Enumerator.prototype._eachEntry = function (entry, i) {
+  var c = this._instanceConstructor;
+  var resolve$$ = c.resolve;
+
+  if (resolve$$ === resolve) {
+    var _then = getThen(entry);
+
+    if (_then === then && entry._state !== PENDING) {
+      this._settledAt(entry._state, i, entry._result);
+    } else if (typeof _then !== 'function') {
+      this._remaining--;
+      this._result[i] = entry;
+    } else if (c === Promise) {
+      var promise = new c(noop);
+      handleMaybeThenable(promise, entry, _then);
+      this._willSettleAt(promise, i);
+    } else {
+      this._willSettleAt(new c(function (resolve$$) {
+        return resolve$$(entry);
+      }), i);
+    }
+  } else {
+    this._willSettleAt(resolve$$(entry), i);
+  }
+};
+
+Enumerator.prototype._settledAt = function (state, i, value) {
+  var promise = this.promise;
+
+  if (promise._state === PENDING) {
+    this._remaining--;
+
+    if (state === REJECTED) {
+      _reject(promise, value);
+    } else {
+      this._result[i] = value;
+    }
+  }
+
+  if (this._remaining === 0) {
+    fulfill(promise, this._result);
+  }
+};
+
+Enumerator.prototype._willSettleAt = function (promise, i) {
+  var enumerator = this;
+
+  subscribe(promise, undefined, function (value) {
+    return enumerator._settledAt(FULFILLED, i, value);
+  }, function (reason) {
+    return enumerator._settledAt(REJECTED, i, reason);
+  });
+};
+
+/**
+  `Promise.all` accepts an array of promises, and returns a new promise which
+  is fulfilled with an array of fulfillment values for the passed promises, or
+  rejected with the reason of the first passed promise to be rejected. It casts all
+  elements of the passed iterable to promises as it runs this algorithm.
+
+  Example:
+
+  ```javascript
+  let promise1 = resolve(1);
+  let promise2 = resolve(2);
+  let promise3 = resolve(3);
+  let promises = [ promise1, promise2, promise3 ];
+
+  Promise.all(promises).then(function(array){
+    // The array here would be [ 1, 2, 3 ];
+  });
+  ```
+
+  If any of the `promises` given to `all` are rejected, the first promise
+  that is rejected will be given as an argument to the returned promises's
+  rejection handler. For example:
+
+  Example:
+
+  ```javascript
+  let promise1 = resolve(1);
+  let promise2 = reject(new Error("2"));
+  let promise3 = reject(new Error("3"));
+  let promises = [ promise1, promise2, promise3 ];
+
+  Promise.all(promises).then(function(array){
+    // Code here never runs because there are rejected promises!
+  }, function(error) {
+    // error.message === "2"
+  });
+  ```
+
+  @method all
+  @static
+  @param {Array} entries array of promises
+  @param {String} label optional string for labeling the promise.
+  Useful for tooling.
+  @return {Promise} promise that is fulfilled when all `promises` have been
+  fulfilled, or rejected if any of them become rejected.
+  @static
+*/
+function all(entries) {
+  return new Enumerator(this, entries).promise;
+}
+
+/**
+  `Promise.race` returns a new promise which is settled in the same way as the
+  first passed promise to settle.
+
+  Example:
+
+  ```javascript
+  let promise1 = new Promise(function(resolve, reject){
+    setTimeout(function(){
+      resolve('promise 1');
+    }, 200);
+  });
+
+  let promise2 = new Promise(function(resolve, reject){
+    setTimeout(function(){
+      resolve('promise 2');
+    }, 100);
+  });
+
+  Promise.race([promise1, promise2]).then(function(result){
+    // result === 'promise 2' because it was resolved before promise1
+    // was resolved.
+  });
+  ```
+
+  `Promise.race` is deterministic in that only the state of the first
+  settled promise matters. For example, even if other promises given to the
+  `promises` array argument are resolved, but the first settled promise has
+  become rejected before the other promises became fulfilled, the returned
+  promise will become rejected:
+
+  ```javascript
+  let promise1 = new Promise(function(resolve, reject){
+    setTimeout(function(){
+      resolve('promise 1');
+    }, 200);
+  });
+
+  let promise2 = new Promise(function(resolve, reject){
+    setTimeout(function(){
+      reject(new Error('promise 2'));
+    }, 100);
+  });
+
+  Promise.race([promise1, promise2]).then(function(result){
+    // Code here never runs
+  }, function(reason){
+    // reason.message === 'promise 2' because promise 2 became rejected before
+    // promise 1 became fulfilled
+  });
+  ```
+
+  An example real-world use case is implementing timeouts:
+
+  ```javascript
+  Promise.race([ajax('foo.json'), timeout(5000)])
+  ```
+
+  @method race
+  @static
+  @param {Array} promises array of promises to observe
+  Useful for tooling.
+  @return {Promise} a promise which settles in the same way as the first passed
+  promise to settle.
+*/
+function race(entries) {
+  /*jshint validthis:true */
+  var Constructor = this;
+
+  if (!isArray(entries)) {
+    return new Constructor(function (_, reject) {
+      return reject(new TypeError('You must pass an array to race.'));
+    });
+  } else {
+    return new Constructor(function (resolve, reject) {
+      var length = entries.length;
+      for (var i = 0; i < length; i++) {
+        Constructor.resolve(entries[i]).then(resolve, reject);
+      }
+    });
+  }
+}
+
+/**
+  `Promise.reject` returns a promise rejected with the passed `reason`.
+  It is shorthand for the following:
+
+  ```javascript
+  let promise = new Promise(function(resolve, reject){
+    reject(new Error('WHOOPS'));
+  });
+
+  promise.then(function(value){
+    // Code here doesn't run because the promise is rejected!
+  }, function(reason){
+    // reason.message === 'WHOOPS'
+  });
+  ```
+
+  Instead of writing the above, your code now simply becomes the following:
+
+  ```javascript
+  let promise = Promise.reject(new Error('WHOOPS'));
+
+  promise.then(function(value){
+    // Code here doesn't run because the promise is rejected!
+  }, function(reason){
+    // reason.message === 'WHOOPS'
+  });
+  ```
+
+  @method reject
+  @static
+  @param {Any} reason value that the returned promise will be rejected with.
+  Useful for tooling.
+  @return {Promise} a promise rejected with the given `reason`.
+*/
+function reject(reason) {
+  /*jshint validthis:true */
+  var Constructor = this;
+  var promise = new Constructor(noop);
+  _reject(promise, reason);
+  return promise;
+}
+
+function needsResolver() {
+  throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
+}
+
+function needsNew() {
+  throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
+}
+
+/**
+  Promise objects represent the eventual result of an asynchronous operation. The
+  primary way of interacting with a promise is through its `then` method, which
+  registers callbacks to receive either a promise's eventual value or the reason
+  why the promise cannot be fulfilled.
+
+  Terminology
+  -----------
+
+  - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
+  - `thenable` is an object or function that defines a `then` method.
+  - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
+  - `exception` is a value that is thrown using the throw statement.
+  - `reason` is a value that indicates why a promise was rejected.
+  - `settled` the final resting state of a promise, fulfilled or rejected.
+
+  A promise can be in one of three states: pending, fulfilled, or rejected.
+
+  Promises that are fulfilled have a fulfillment value and are in the fulfilled
+  state.  Promises that are rejected have a rejection reason and are in the
+  rejected state.  A fulfillment value is never a thenable.
+
+  Promises can also be said to *resolve* a value.  If this value is also a
+  promise, then the original promise's settled state will match the value's
+  settled state.  So a promise that *resolves* a promise that rejects will
+  itself reject, and a promise that *resolves* a promise that fulfills will
+  itself fulfill.
+
+
+  Basic Usage:
+  ------------
+
+  ```js
+  let promise = new Promise(function(resolve, reject) {
+    // on success
+    resolve(value);
+
+    // on failure
+    reject(reason);
+  });
+
+  promise.then(function(value) {
+    // on fulfillment
+  }, function(reason) {
+    // on rejection
+  });
+  ```
+
+  Advanced Usage:
+  ---------------
+
+  Promises shine when abstracting away asynchronous interactions such as
+  `XMLHttpRequest`s.
+
+  ```js
+  function getJSON(url) {
+    return new Promise(function(resolve, reject){
+      let xhr = new XMLHttpRequest();
+
+      xhr.open('GET', url);
+      xhr.onreadystatechange = handler;
+      xhr.responseType = 'json';
+      xhr.setRequestHeader('Accept', 'application/json');
+      xhr.send();
+
+      function handler() {
+        if (this.readyState === this.DONE) {
+          if (this.status === 200) {
+            resolve(this.response);
+          } else {
+            reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
+          }
+        }
+      };
+    });
+  }
+
+  getJSON('/posts.json').then(function(json) {
+    // on fulfillment
+  }, function(reason) {
+    // on rejection
+  });
+  ```
+
+  Unlike callbacks, promises are great composable primitives.
+
+  ```js
+  Promise.all([
+    getJSON('/posts'),
+    getJSON('/comments')
+  ]).then(function(values){
+    values[0] // => postsJSON
+    values[1] // => commentsJSON
+
+    return values;
+  });
+  ```
+
+  @class Promise
+  @param {function} resolver
+  Useful for tooling.
+  @constructor
+*/
+function Promise(resolver) {
+  this[PROMISE_ID] = nextId();
+  this._result = this._state = undefined;
+  this._subscribers = [];
+
+  if (noop !== resolver) {
+    typeof resolver !== 'function' && needsResolver();
+    this instanceof Promise ? initializePromise(this, resolver) : needsNew();
+  }
+}
+
+Promise.all = all;
+Promise.race = race;
+Promise.resolve = resolve;
+Promise.reject = reject;
+Promise._setScheduler = setScheduler;
+Promise._setAsap = setAsap;
+Promise._asap = asap;
+
+Promise.prototype = {
+  constructor: Promise,
+
+  /**
+    The primary way of interacting with a promise is through its `then` method,
+    which registers callbacks to receive either a promise's eventual value or the
+    reason why the promise cannot be fulfilled.
+  
+    ```js
+    findUser().then(function(user){
+      // user is available
+    }, function(reason){
+      // user is unavailable, and you are given the reason why
+    });
+    ```
+  
+    Chaining
+    --------
+  
+    The return value of `then` is itself a promise.  This second, 'downstream'
+    promise is resolved with the return value of the first promise's fulfillment
+    or rejection handler, or rejected if the handler throws an exception.
+  
+    ```js
+    findUser().then(function (user) {
+      return user.name;
+    }, function (reason) {
+      return 'default name';
+    }).then(function (userName) {
+      // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
+      // will be `'default name'`
+    });
+  
+    findUser().then(function (user) {
+      throw new Error('Found user, but still unhappy');
+    }, function (reason) {
+      throw new Error('`findUser` rejected and we're unhappy');
+    }).then(function (value) {
+      // never reached
+    }, function (reason) {
+      // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
+      // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
+    });
+    ```
+    If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
+  
+    ```js
+    findUser().then(function (user) {
+      throw new PedagogicalException('Upstream error');
+    }).then(function (value) {
+      // never reached
+    }).then(function (value) {
+      // never reached
+    }, function (reason) {
+      // The `PedgagocialException` is propagated all the way down to here
+    });
+    ```
+  
+    Assimilation
+    ------------
+  
+    Sometimes the value you want to propagate to a downstream promise can only be
+    retrieved asynchronously. This can be achieved by returning a promise in the
+    fulfillment or rejection handler. The downstream promise will then be pending
+    until the returned promise is settled. This is called *assimilation*.
+  
+    ```js
+    findUser().then(function (user) {
+      return findCommentsByAuthor(user);
+    }).then(function (comments) {
+      // The user's comments are now available
+    });
+    ```
+  
+    If the assimliated promise rejects, then the downstream promise will also reject.
+  
+    ```js
+    findUser().then(function (user) {
+      return findCommentsByAuthor(user);
+    }).then(function (comments) {
+      // If `findCommentsByAuthor` fulfills, we'll have the value here
+    }, function (reason) {
+      // If `findCommentsByAuthor` rejects, we'll have the reason here
+    });
+    ```
+  
+    Simple Example
+    --------------
+  
+    Synchronous Example
+  
+    ```javascript
+    let result;
+  
+    try {
+      result = findResult();
+      // success
+    } catch(reason) {
+      // failure
+    }
+    ```
+  
+    Errback Example
+  
+    ```js
+    findResult(function(result, err){
+      if (err) {
+        // failure
+      } else {
+        // success
+      }
+    });
+    ```
+  
+    Promise Example;
+  
+    ```javascript
+    findResult().then(function(result){
+      // success
+    }, function(reason){
+      // failure
+    });
+    ```
+  
+    Advanced Example
+    --------------
+  
+    Synchronous Example
+  
+    ```javascript
+    let author, books;
+  
+    try {
+      author = findAuthor();
+      books  = findBooksByAuthor(author);
+      // success
+    } catch(reason) {
+      // failure
+    }
+    ```
+  
+    Errback Example
+  
+    ```js
+  
+    function foundBooks(books) {
+  
+    }
+  
+    function failure(reason) {
+  
+    }
+  
+    findAuthor(function(author, err){
+      if (err) {
+        failure(err);
+        // failure
+      } else {
+        try {
+          findBoooksByAuthor(author, function(books, err) {
+            if (err) {
+              failure(err);
+            } else {
+              try {
+                foundBooks(books);
+              } catch(reason) {
+                failure(reason);
+              }
+            }
+          });
+        } catch(error) {
+          failure(err);
+        }
+        // success
+      }
+    });
+    ```
+  
+    Promise Example;
+  
+    ```javascript
+    findAuthor().
+      then(findBooksByAuthor).
+      then(function(books){
+        // found books
+    }).catch(function(reason){
+      // something went wrong
+    });
+    ```
+  
+    @method then
+    @param {Function} onFulfilled
+    @param {Function} onRejected
+    Useful for tooling.
+    @return {Promise}
+  */
+  then: then,
+
+  /**
+    `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
+    as the catch block of a try/catch statement.
+  
+    ```js
+    function findAuthor(){
+      throw new Error('couldn't find that author');
+    }
+  
+    // synchronous
+    try {
+      findAuthor();
+    } catch(reason) {
+      // something went wrong
+    }
+  
+    // async with promises
+    findAuthor().catch(function(reason){
+      // something went wrong
+    });
+    ```
+  
+    @method catch
+    @param {Function} onRejection
+    Useful for tooling.
+    @return {Promise}
+  */
+  'catch': function _catch(onRejection) {
+    return this.then(null, onRejection);
+  }
+};
+
+function polyfill() {
+    var local = undefined;
+
+    if (typeof global !== 'undefined') {
+        local = global;
+    } else if (typeof self !== 'undefined') {
+        local = self;
+    } else {
+        try {
+            local = Function('return this')();
+        } catch (e) {
+            throw new Error('polyfill failed because global object is unavailable in this environment');
+        }
+    }
+
+    var P = local.Promise;
+
+    if (P) {
+        var promiseToString = null;
+        try {
+            promiseToString = Object.prototype.toString.call(P.resolve());
+        } catch (e) {
+            // silently ignored
+        }
+
+        if (promiseToString === '[object Promise]' && !P.cast) {
+            return;
+        }
+    }
+
+    local.Promise = Promise;
+}
+
+// Strange compat..
+Promise.polyfill = polyfill;
+Promise.Promise = Promise;
+
+return Promise;
+
+})));
+
+ES6Promise.polyfill();
+//# sourceMappingURL=es6-promise.auto.map

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 5 - 0
src/js/lib/vuejs/vue.min.js


+ 24 - 0
test/NodeRunner.js

@@ -0,0 +1,24 @@
+/**
+ * NodeRunner.js
+ *
+ * The purpose of this file is to execute via PhantomJS the file
+ * PhantomRunner.js, because PhantomJS is managed by node.
+ *
+ * @author tlwr [toby@toby.codes
+ *
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ *
+ */
+var path = require("path");
+var phantomjs = require("phantomjs-prebuilt");
+
+var phantomEntryPoint = path.join(__dirname, "PhantomRunner.js");
+var program = phantomjs.exec(phantomEntryPoint);
+
+program.stdout.pipe(process.stdout);
+program.stderr.pipe(process.stderr);
+
+program.on("exit", function(status) {
+    process.exit(status);
+});

+ 80 - 0
test/PhantomRunner.js

@@ -0,0 +1,80 @@
+/**
+ * PhantomRunner.js
+ *
+ * This file navigates to build/test/index.html and logs the test results.
+ *
+ * @author tlwr [toby@toby.codes
+ *
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ *
+ */
+var page = require("webpage").create();
+
+var allTestsPassing = true;
+var testStatusCounts = {
+    total: 0,
+};
+
+function statusToIcon(status) {
+    var icons = {
+        erroring: "🔥",
+        failing: "❌",
+        passing: "✔️️",
+    };
+    return icons[status] || "?";
+}
+
+page.onCallback = function(messageType) {
+    if (messageType === "testResult") {
+        var testResult = arguments[1];
+
+        allTestsPassing = allTestsPassing && testResult.status === "passing";
+        var newCount = (testStatusCounts[testResult.status] || 0) + 1;
+        testStatusCounts[testResult.status] = newCount;
+        testStatusCounts.total += 1;
+
+        console.log([
+            statusToIcon(testResult.status),
+            testResult.test.name
+        ].join(" "));
+
+        if (testResult.output) {
+            console.log(
+                testResult.output
+                    .trim()
+                    .replace(/^/, "\t")
+                    .replace(/\n/g, "\n\t")
+            );
+        }
+    } else if (messageType === "exit") {
+
+        console.log("\n");
+
+        for (var testStatus in testStatusCounts) {
+            var count = testStatusCounts[testStatus];
+            if (count > 0) {
+                console.log(testStatus.toUpperCase(), count);
+            }
+        }
+
+        if (!allTestsPassing) {
+            console.log("\n")
+            console.log("Not all tests are passing");
+        }
+
+        phantom.exit(allTestsPassing ? 0 : 1);
+    }
+};
+
+page.open("file:///home/toby/Code/CyberChef/build/test/index.html", function(status) {
+    if (status !== "success") {
+        console.log("STATUS", status);
+        phantom.exit(1);
+    }
+});
+
+setTimeout(function() {
+    // Timeout
+    phantom.exit(1);
+}, 10 * 1000);

+ 96 - 0
test/TestRegister.js

@@ -0,0 +1,96 @@
+/**
+ * TestRegister.js
+ *
+ * This is so individual files can register their tests in one place, and
+ * ensure that they will get run by the frontend.
+ *
+ * @author tlwr [toby@toby.codes
+ *
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ *
+ */
+(function() {
+    /**
+     * Add a list of tests to the register.
+     *
+     * @class
+     */
+    function TestRegister() {
+        this.tests = [];
+    }
+
+    /**
+     * Add a list of tests to the register.
+     *
+     * @param {Object[]} tests
+     */
+    TestRegister.prototype.addTests = function(tests) {
+        this.tests = this.tests.concat(tests);
+    };
+
+    /**
+     * Returns the list of tests.
+     *
+     * @returns {Object[]} tests
+     */
+    TestRegister.prototype.getTests = function() {
+        return this.tests;
+    };
+
+    /**
+     * Runs all the tests in the register.
+     *
+     */
+    TestRegister.prototype.runTests = function() {
+        return Promise.all(
+            this.tests.map(function(test, i) {
+                var chef = new Chef();
+
+                return Promise.resolve(chef.bake(
+                    test.input,
+                    test.recipeConfig,
+                    {},
+                    0,
+                    0
+                ))
+                .then(function(result) {
+                    var ret = {
+                        test: test,
+                        status: null,
+                        output: null,
+                    };
+
+                    if (result.error) {
+                        if (test.expectedError) {
+                            ret.status = "passing";
+                        } else {
+                            ret.status = "erroring";
+                            ret.output = result.error.displayStr;
+                        }
+                    } else {
+                        if (test.expectedError) {
+                            ret.status = "failing";
+                            ret.output = "Expected an error but did not receive one.";
+                        } else if (result.result === test.expectedOutput) {
+                            ret.status = "passing";
+                        } else {
+                            ret.status = "failing";
+                            ret.output = [
+                                "Expected",
+                                "\t" + test.expectedOutput.replace(/\n/g, "\n\t"),
+                                "Received",
+                                "\t" + result.result.replace(/\n/g, "\n\t"),
+                            ].join("\n");
+                        }
+                    }
+
+                    return ret;
+                });
+            })
+        );
+    };
+
+    // Singleton TestRegister, keeping things simple and obvious.
+    window.TestRegister = new TestRegister();
+})();

+ 38 - 0
test/TestRunner.js

@@ -0,0 +1,38 @@
+/**
+ * TestRunner.js
+ *
+ * This is for actually running the tests in the test register.
+ *
+ * @author tlwr [toby@toby.codes
+ *
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ *
+ */
+(function() {
+    document.addEventListener("DOMContentLoaded", function() {
+        TestRegister.runTests()
+        .then(function(results) {
+            results.forEach(function(testResult) {
+                if (typeof window.callPhantom === "function") {
+                    window.callPhantom(
+                        "testResult",
+                        testResult
+                    );
+                } else {
+                    var output = [
+                        "----------",
+                        testResult.test.name,
+                        testResult.status,
+                        testResult.output,
+                    ].join("<br>");
+                    document.body.innerHTML += "<div>" + output + "</div>";
+                }
+            });
+
+            if (typeof window.callPhantom === "function") {
+                window.callPhantom("exit");
+            }
+        });
+    });
+})();

+ 95 - 0
test/tests/core.js

@@ -0,0 +1,95 @@
+/**
+ * Core tests.
+ *
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ *
+ */
+TestRegister.addTests([
+    //{
+    //    name: "Example error",
+    //    input: "1\n2\na\n4",
+    //    expectedOutput: "1\n2\n3\n4",
+    //    recipeConfig: [
+    //        {
+    //            op: "Fork",
+    //            args: ["\n", "\n", false],
+    //        },
+    //        {
+    //            op: "To Base",
+    //            args: [16],
+    //        },
+    //    ],
+    //},
+    //{
+    //    name: "Example non-error when error was expected",
+    //    input: "1",
+    //    expectedError: true,
+    //    recipeConfig: [
+    //        {
+    //            op: "To Base",
+    //            args: [16],
+    //        },
+    //    ],
+    //},
+    //{
+    //    name: "Example fail",
+    //    input: "1\n2\na\n4",
+    //    expectedOutput: "1\n2\n3\n4",
+    //    recipeConfig: [
+    //        {
+    //            op: "Fork",
+    //            args: ["\n", "\n", true],
+    //        },
+    //        {
+    //            op: "To Base",
+    //            args: [16],
+    //        },
+    //    ],
+    //},
+    {
+        name: "Fork: nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                op: "Fork",
+                args: ["\n", "\n", false],
+            },
+        ],
+    },
+    {
+        name: "Fork, Merge: nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                op: "Fork",
+                args: ["\n", "\n", false],
+            },
+            {
+                op: "Merge",
+                args: [],
+            },
+        ],
+    },
+    {
+        name: "Fork, (expect) Error, Merge",
+        input: "1\n2\na\n4",
+        expectedError: true,
+        recipeConfig: [
+            {
+                op: "Fork",
+                args: ["\n", "\n", false],
+            },
+            {
+                op: "To Base",
+                args: [16],
+            },
+            {
+                op: "Merge",
+                args: [],
+            },
+        ],
+    },
+]);

+ 77 - 0
test/tests/operations/Base58.js

@@ -0,0 +1,77 @@
+/**
+ * Base58 tests.
+ *
+ * @author tlwr [toby@toby.codes
+ *
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ *
+ */
+TestRegister.addTests([
+    {
+        name: "To Base58 (Bitcoin): nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                op: "To Base58",
+                args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"],
+            },
+        ],
+    },
+    {
+        name: "To Base58 (Ripple): nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                op: "To Base58",
+                args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"],
+            },
+        ],
+    },
+    {
+        name: "To Base58 (Bitcoin): 'hello world'",
+        input: "hello world",
+        expectedOutput: "StV1DL6CwTryKyV",
+        recipeConfig: [
+            {
+                op: "To Base58",
+                args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"],
+            },
+        ],
+    },
+    {
+        name: "To Base58 (Ripple): 'hello world'",
+        input: "hello world",
+        expectedOutput: "StVrDLaUATiyKyV",
+        recipeConfig: [
+            {
+                op: "To Base58",
+                args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"],
+            },
+        ],
+    },
+    {
+        name: "From Base58 (Bitcoin): 'StV1DL6CwTryKyV'",
+        input: "StV1DL6CwTryKyV",
+        expectedOutput: "hello world",
+        recipeConfig: [
+            {
+                op: "From Base58",
+                args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"],
+            },
+        ],
+    },
+    {
+        name: "From Base58 (Ripple): 'StVrDLaUATiyKyV'",
+        input: "StVrDLaUATiyKyV",
+        expectedOutput: "hello world",
+        recipeConfig: [
+            {
+                op: "From Base58",
+                args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"],
+            },
+        ],
+    },
+]);

+ 33 - 0
test/tests/operations/MorseCode.js

@@ -0,0 +1,33 @@
+/**
+ * Base58 tests.
+ *
+ * @author tlwr [toby@toby.codes
+ *
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ *
+ */
+TestRegister.addTests([
+    {
+        name: "To Morse Code: 'SOS'",
+        input: "SOS",
+        expectedOutput: "... --- ...",
+        recipeConfig: [
+            {
+                op: "To Morse Code",
+                args: ["-/.", "Space", "Line feed"],
+            },
+        ],
+    },
+    {
+        name: "From Morse Code '... --- ...'",
+        input: "... --- ...",
+        expectedOutput: "SOS",
+        recipeConfig: [
+            {
+                op: "From Morse Code",
+                args: ["Space", "Line feed"],
+            },
+        ],
+    },
+]);

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott