Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

fix stack #9

Closed
wants to merge 2 commits into from

1 participant

@qRoC

No description provided.

@qRoC

Safari 7:

console.log( new Error() )

[Log] Error
__proto__: Error
constructor: function Error() {
message: ""
name: "Error"
toString: function toString() {
__proto__: Object

StackTrace имеют только браузеры на движке v8, firefox, ie10+.
message содержит описание ошибки, так что или так или getTrace вообще ничего возвращать не должен.

@qRoC

gj

@qRoC qRoC closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 10, 2013
  1. @qRoC

    fix stack

    qRoC authored
  2. @qRoC

    update sample

    qRoC authored
This page is out of date. Refresh to see the latest.
View
BIN  example/.DS_Store
Binary file not shown
View
4 example/index.html
@@ -117,8 +117,8 @@ <h1 class="logo">test<span>.</span>it</h1>
test.done();
</pre><p class=suggestion>// If you are using Firebug - reload the page, if console is blank.</p>
<!-- framework -->
- <script src='./testit.v1.0.2.min.js'></script>
+ <script src='testit.js'></script>
<!-- script which is represented above -->
- <script src='./example.js'></script>
+ <script src='example.js'></script>
</body>
</html>
View
953 example/testit.js
@@ -0,0 +1,953 @@
+(function(window) {
+
+var testit = function() {
+ /**
+ * group class, which will contain tests
+ * In addition, it will be used for wrapping some wrong code from falling.
+ * @constructor
+ * @private
+ * @attribute {String} type type of object ('group' or 'test')
+ * @attribute {String} name name of group
+ * @attribute {String} status indicate results of all test in group ('pass','fail','error')
+ * @attribute {String} comment text specified by user
+ * @attribute {Error} error contain error object if some of tests throw it
+ * @attribute {Number} time time in ms spend on code in group
+ * @attribute {Object} result counters for tests and groups
+ * @attribute {array} stack array of tests and groups
+ */
+ var group = function() {
+ this.type = 'group';
+ this.name = undefined;
+ this.status = undefined;
+ this.comment = undefined;
+ this.error = undefined;
+ this.time = 0;
+ this.result = {
+ pass: 0,
+ fail: 0,
+ error: 0,
+ total: 0
+ };
+ this.stack = [];
+ }
+
+ /**
+ * test class, which will contain result and some more info about one test
+ * @constructor
+ * @private
+ * @attribute {String} type type of object ('group' or 'test')
+ * @attribute {String} status indicate results of test ('pass','fail','error')
+ * @attribute {String} comment text specified by user
+ * @attribute {String} description text generated by script
+ * @attribute {Error} error contain error object if test can throw it without falling
+ * @attribute {Number} time time in ms spend on test
+ * @attribute {Array} argument all received arguments
+ */
+ var test = function() {
+ this.type = 'test';
+ this.status = undefined;
+ this.comment = undefined;
+ this.description = undefined;
+ this.error = undefined;
+ // this.time = new Date().getTime();
+ this.argument = [];
+ }
+
+ /**
+ * main group
+ * @public
+ * @type {group}
+ */
+ var root = new group();
+ this.root = root;
+ root.name = 'root';
+ root.time = new Date().getTime();
+
+ /**
+ * make new instace of group, fill it, add it to previous group.stack, fill some values in previous group
+ * @private
+ * @chainable
+ * @param {String} name name of new group
+ * @param {Function} fun function witch will be tryed to execute (commonly consist of tests and other groups)
+ * @return {Object} test with link
+ */
+ var _makeGroup = function(name,fun) {
+ switch (arguments.length) {
+ case 0 : throw new RangeError("test.group expect at least 1 argument");
+ case 1 : {
+ var stack = (this.link)? this.link.stack : root.stack;
+ for (var i in stack) {
+ if (stack[i].type !== 'group') continue;
+ if (stack[i].name === name) {
+ return Object.create(this,{link:{value:stack[i]}});
+ }
+ }
+ throw new ReferenceError('there are no group with name: '+name);
+ } break;
+ case 2 : break;
+ default : throw new RangeError("test.group expect maximum of 2 arguments");
+ }
+
+ /** get timestamp */
+ var time = new Date().getTime();
+
+ /** making a new root, to provide chaining */
+ var oldRootChain = root;
+ root = (this.link)? this.link : root;
+ /** var for the new instance of group */
+ var newgroup;
+ /** identify new group */
+ var groupAlreadyExist = false;
+ /** find group in current-level stack */
+ for (var i in root.stack) {
+ if (root.stack[i].type !== 'group') continue;
+ if (root.stack[i].name === name) {
+ newgroup = root.stack[i];
+ groupAlreadyExist = true;
+ break;
+ }
+ }
+ if (!groupAlreadyExist) newgroup = new group();
+ newgroup.name = name;
+
+ /** add backlink to provide trek back */
+ newgroup.linkBack = root;
+
+ /** set to pass as default. it's may be changed in some next lines */
+ var oldstatus;
+ if (groupAlreadyExist) oldstatus = newgroup.status;
+ newgroup.status ='pass';
+
+
+ /**
+ * making a new root, to provide nesting
+ * Nested tests and groups will us it, like first one use root.
+ */
+ var oldRootNest = root;
+ root = newgroup;
+ /**
+ * try to execute code with tests and other groups in it
+ * This part provide nesting.
+ */
+ try{
+ fun();
+ } catch(e) {
+ newgroup.status = 'error';
+ var errorObject = {};
+ generateError(e,errorObject);
+
+ newgroup.error = errorObject;
+ }
+ /**
+ * reverse inheritance of status
+ * If some of deep nested test will 'fail', root will be 'fail' too.
+ * More info in updateStatus() comments.
+ */
+ // oldRootNest.status = updateStatus(oldRootNest.status,root.status);
+ // oldRootChain.status = updateStatus(oldRootChain.status,root.status);
+
+ /** take back old root */
+ root = oldRootNest;
+
+ /** take back old root */
+ root = oldRootChain;
+
+
+ /** update time */
+ newgroup.time += new Date().getTime() - time;
+
+
+ /** finally place this group into previous level stack (if it's a new group) */
+ if (!groupAlreadyExist) root.stack.push(newgroup);
+
+ /** update counters */
+ updateCounters(newgroup);
+
+ /** return testit with link to this group to provide chaining */
+ return Object.create(this,{link:{value:newgroup}});
+ }
+ /**
+ * public interface for _makeGroup
+ * @public
+ * @example
+ * test.group('name of group',function(){
+ * test.it('nested test');
+ * test.group('nested group',function(){
+ * test.it('deep nested test');
+ * });
+ * });
+ */
+ this.group = _makeGroup;
+
+ /**
+ * basic test. Make new instance of test, fill it, add it to previous group.stack, fill some values in previous group
+ * @private
+ * @chainable
+ * @param {Multiple} a @required first argument, which will check for truth it only transmitted
+ * @param {Multiple} b second argument which will compared with a if transmitted
+ * @return {Object} test with link
+ */
+ var _it = function(a,b) {
+ /**
+ * making a new instance of test
+ * Most of code in this function will manipulate whis it.
+ */
+ var newtest = new test();
+
+ /**
+ * fill newtest.argument with arguments
+ * (arguments is array-like object, but not array. So i can't just newtest.argument = newtest.argument.concat(arguments); or newtest.argument = arguments)
+ */
+ for (var i in arguments) {
+ newtest.argument.push(arguments[i]);
+ }
+ /** try to figure out what kind of test expected */
+ switch (arguments.length) {
+ /** in case of no arguments - throw Reference error */
+ case 0 : {
+ newtest.status = 'error';
+ var e = new RangeError("at least one argument expected");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } break;
+ /** if there only one argument - test it for truth */
+ case 1 : {
+ testNonFalse(newtest,[a]);
+ } break;
+ /** if there are two arguments - test equalence between them */
+ case 2 : {
+ testEquivalence(newtest,[a,b]);
+ } break;
+ /** otherwise throw Range error */
+ default : {
+ newtest.status = 'error';
+ var e = new RangeError("too much arguments");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ }
+ }
+
+ /** finally place this test into container stack */
+ root.stack.push(newtest);
+
+ /** update counters of contained object */
+ updateCounters(root);
+
+ /** return testit with link to this test to provide chaining */
+ return Object.create(this,{link:{value:newtest}});
+ }
+ /**
+ * public interface for _it()
+ * @public
+ * @example
+ * test.it(someThing);
+ * test.it(myFunction());
+ * test.it(myVar>5);
+ * test.it(myVar,mySecondVar);
+ */
+ this.it = _it;
+
+ /**
+ * test array of values for non-false
+ * @private
+ * @chainable
+ * @param {Array} args array of values which will be tested
+ * @return {Object} test with link
+ */
+ var _them = function(args) {
+ /**
+ * making a new instance of test
+ * Most of code in this function will manipulate whis it.
+ */
+ var newtest = new test();
+
+ for (var i in arguments) {
+ newtest.argument.push(arguments[i]);
+ }
+
+ /** throw error if argument is not array */
+ if (arguments.length === 0 || arguments.length > 1) {
+ newtest.status = 'error';
+ var e = new RangeError("test.them expects exactly 1 argument");
+ var errorObject = {};
+ generateError(e,errorObject);
+
+ newtest.error = errorObject;
+ } else if (_typeof(args) !== 'Array') {
+ newtest.status = 'error';
+ var e = new TypeError("test.them expects to receive an array");
+ var errorObject = {};
+ generateError(e,errorObject);
+
+ newtest.error = errorObject;
+ } else {
+ testNonFalse(newtest,args);
+ }
+
+ /** finally place this test into container stack */
+ root.stack.push(newtest);
+
+ /** update counters of contained object */
+ updateCounters(root);
+
+ /** return testit with link to this test to provide chaining */
+ return Object.create(this,{link:{value:newtest}});
+ }
+ /**
+ * public interface for _them()
+ * @public
+ * @example
+ * test.them([1,'a',true,window]);
+ */
+ this.them = _them;
+
+ /**
+ * test type of first argument (value) to be equal to secon argument
+ * @private
+ * @chainable
+ * @param {Multiple} value will be tested
+ * @param {[type]} type type to compare with
+ */
+ var _type = function(value,type) {
+ /**
+ * making a new instance of test
+ * Most of code in this function will manipulate whis it.
+ */
+ var newtest = new test();
+ /**
+ * fill newtest.argument with arguments
+ * (arguments is array-like object, but not array. So i can't just newtest.argument = newtest.argument.concat(arguments); or newtest.argument = arguments)
+ */
+ for (var i in arguments) {
+ newtest.argument.push(arguments[i]);
+ }
+ /** throw error if there are not 2 arguments */
+
+ if (arguments.length!==2) {
+ newtest.status = 'error';
+ var e = new RangeError("test.type expect two arguments");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } else if (_typeof(type) !== 'String') {
+ newtest.status = 'error';
+ var e = new TypeError("second argument must be a String");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } else if (!arrayConsist(identifiedTypes,type.toLowerCase())) {
+ newtest.status = 'error';
+ var e = new TypeError("second argument must be a standart type");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } else {
+ testType(newtest,[value],type);
+ }
+
+ /** finally place this test into container stack */
+ root.stack.push(newtest);
+
+ /** update counters of contained object */
+ updateCounters(root);
+
+ /** return testit with link to this test to provide chaining */
+ return Object.create(this,{link:{value:newtest}});
+ }
+ /**
+ * public interface for _type()
+ * @public
+ * @example
+ * test.type('asd','String');
+ */
+ this.type = _type;
+
+ /**
+ * compare types of all values in args between each other and 'type' if defined
+ * @private
+ * @chainable
+ * @param {Array} args array of values which will be tested
+ * @param {String} type type which will be compared whith
+ */
+ var _types = function(args,type) {
+ /**
+ * making a new instance of test
+ * Most of code in this function will manipulate whis it.
+ */
+ var newtest = new test();
+ /**
+ * fill newtest.argument with arguments
+ * (arguments is array-like object, but not array. So i can't just newtest.argument = newtest.argument.concat(arguments); or newtest.argument = arguments)
+ */
+ for (var i in arguments) {
+ newtest.argument.push(arguments[i]);
+ }
+ /** throw error if there are not 2 arguments */
+ if (arguments.length>2) {
+ newtest.status = 'error';
+ var e = new RangeError("test.types expect maximum of two arguments");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } else if (_typeof(args) !== 'Array') {
+ newtest.status = 'error';
+ var e = new TypeError("test.types expect array in first argument");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } else if (type) {
+ if (_typeof(type) !== 'String') {
+ newtest.status = 'error';
+ var e = new TypeError("second argument must be a String");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } else if (!arrayConsist(identifiedTypes,type.toLowerCase())) {
+ newtest.status = 'error';
+ var e = new TypeError("second argument must be a standart type");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } else {
+ testType(newtest,args,type);
+ }
+ } else if (args.length<2) {
+ newtest.status = 'error';
+ var e = new RangeError("test.types expect array with minimum 2 values, if second argument not defined");
+ var errorObject = {};
+ generateError(e,errorObject);
+ newtest.error = errorObject;
+ } else {
+ testType(newtest,args);
+ }
+
+ /** finally place this test into container stack */
+ root.stack.push(newtest);
+
+ /** update counters of contained object */
+ updateCounters(root);
+
+ /** return testit with link to this test to provide chaining */
+ return Object.create(this,{link:{value:newtest}});
+ }
+ /**
+ * public interface for _types
+ * @public
+ * @example
+ * test.type([1,2,3],'Number');
+ */
+ this.types = _types;
+
+ /**
+ * Test types of all values in args
+ * @private
+ * @param {Objcet} test will be updated
+ * @param {Array} args consist values which types will be tested
+ * @param {[type]} type consist type which will be compared with
+ */
+ var testType = function(test,args,type) {
+ test.description = 'type of argument is ';
+
+ if (!type) type = _typeof(args[0]);
+
+ for (arg in args) {
+ if (_typeof(args[arg]).toLowerCase() !== type.toLowerCase()) {
+ test.description += 'not '+type;
+ test.status = 'fail';
+ return
+ }
+ }
+
+ test.description += type;
+ test.status = 'pass';
+ }
+
+ /**
+ * Test all values in args for non-false
+ * @private
+ * @param {Objcet} test will be updated
+ * @param {Array} args consist values which will be tested
+ */
+ var testNonFalse = function(test,args) {
+ /** use different text when one and multiple values are tested */
+ test.description = (args.length===1)? 'argument is not ':'arguments is not ';
+
+ /** test every value in args */
+ for (arg in args) {
+ if (!args[arg]) {
+ test.description += 'true';
+ test.status = 'fail';
+ return;
+ }
+ }
+
+ /** if code not stopped in previous step, test passed */
+ test.description += 'false';
+ test.status = 'pass';
+ }
+
+ /**
+ * Test all values in args for equivalence
+ * @private
+ * @param {Objcet} test will be updated
+ * @param {Array} args consist values which will be tested
+ */
+ var testEquivalence = function(test,args) {
+ /** first value will be used as model in comparissons */
+ var model = args.shift();
+
+ /** compare all types of values in args with type of model */
+ var type = _typeof(model);
+ for (arg in args) {
+ if (_typeof(args[arg]) !== type) {
+ test.status = 'fail';
+ test.description = 'arguments has different types';
+ return;
+ }
+ }
+
+ /** compare all values in args with model */
+ for (arg in args) {
+ if (!deepCompare(model,args[arg])) {
+ test.description = 'arguments are not equal';
+ test.status = 'fail';
+ return;
+ }
+ }
+
+ /** if code not stopped earlier, test passed */
+ test.description = 'arguments are equal';
+ test.status = 'pass';
+ }
+
+ /**
+ * add comment for the linked test or group
+ * @private
+ * @chainable
+ * @type {Function}
+ * @param {String} text user defined text, which will be used as a comment
+ */
+ var _comment = function(text) {
+ /** add comment, if there are something can be commented */
+ if (!this.link) throw new ReferenceError('comment can only be used in testit chain');
+ this.link.comment = text;
+
+ return this;
+ }
+ /**
+ * public interface for _comment()
+ * @public
+ * @example
+ * test.group('group name',function(){
+ * test
+ * .it(someThing)
+ * .comment('comment to test');
+ * }).comment('comment to group');
+ */
+ this.comment = _comment;
+
+ /**
+ * try to execute functions in arguments, depend on test|group result
+ * @private
+ * @chainable
+ * @param {Function} pass function to execute if test|group passed
+ * @param {Function} fail function to execute if test|group failed
+ * @param {Function} error function to execute if test|group cause error
+ */
+ var _callback = function(pass,fail,error) {
+ if (!this.link) throw new ReferenceError('callback can only be used in testit chain');
+ if (this.link.status === 'pass' && _typeof(pass) === 'Function' ) try {pass();} catch(e) {throw e;}
+ if (this.link.status === 'fail' && _typeof(fail) === 'Function' ) try {fail();} catch(e) {throw e;}
+ if (this.link.status === 'error' && _typeof(error) === 'Function' ) try {error();} catch(e) {throw e;}
+
+ return this;
+ }
+ /**
+ * public interface for _callback()
+ * @public
+ * @example
+ * test.it(someThing).callback(
+ * function() {...} // - will be execute if test passed
+ * ,function() {...} // - will be execute if test failed
+ * ,function() {...} // - will be execute if test error
+ * );
+ */
+ this.callback = _callback;
+
+ /**
+ * Final chain-link: will return result of test or group
+ * @private
+ * @return {boolean} true - if test or group passed, false - otherwise.
+ */
+ var _result = function() {
+ if (this.link) {
+ return (this.link.status == 'pass')? true : false;
+ }
+ return undefined;
+ }
+ /**
+ * public interface for _result()
+ * @public
+ * @example
+ * var testResult = test.it(undefined).comment('comment to test').result(); // testResult === false
+ */
+ this.result = _result;
+
+ /**
+ * Final chain-link: will return arguments of test (not of group!)
+ * @private
+ * @return single argument or array of arguments
+ */
+ var _arguments = function() {
+ if (this.link) {
+ if (this.link.type!=='test') return TypeError('groups does not return arguments');
+ switch (this.link.argument.length) {
+ case 0 : return undefined
+ case 1 : return this.link.argument[0];
+ default : return this.link.argument;
+ }
+ }
+ return undefined;
+ }
+ /**
+ * public interface for _arguments()
+ * @public
+ * @example
+ * var testArguments = test.it('single').comment('comment to test').arguments(); // testArguments === 'single'
+ * testArguments = test.it('first','second').comment('comment to test').arguments(); // testArguments === ['first','second']
+ */
+ this.arguments = _arguments;
+
+
+ /**
+ * apply last stuff and display results
+ * type {Function}
+ * @private
+ */
+ var _done = function(obj) {
+ /** update time in root */
+ root.time = new Date().getTime() - root.time;
+
+ /** display root */
+ // console.dir(root);
+ _printConsole(root);
+ }
+ /**
+ * public interface for _done()
+ * @type {Function}
+ * @public
+ * @example
+ * test.it(1);
+ * test.it(2);
+ * test.it(3);
+ *
+ * test.done();
+ */
+ this.done = _done;
+
+
+ /** update counters of contained object */
+ var updateCounters = function(link) {
+ link.result = {
+ pass: 0,
+ fail: 0,
+ error: 0,
+ total: 0
+ };
+ for (var i in link.stack) {
+ link.result.total++;
+ switch (link.stack[i].status) {
+ case 'pass' : {
+ link.result.pass++;
+ } break;
+ case 'fail' : {
+ link.result.fail++;
+ } break;
+ case 'error' : {
+ link.result.error++;
+ } break;
+ };
+ };
+
+ if (link.result.error || link.error) {link.status='error'}
+ else if (link.result.fail) {link.status='fail'}
+ else {link.status='pass'}
+ // console.log(link.name,(link.linkBack)?link.linkBack.name:link.linkBack)
+ if (link.linkBack) {
+ updateCounters(link.linkBack);
+ }
+ }
+
+
+ /**
+ * pritty display group or test in browser dev console
+ * @private
+ * @param {Object} obj group or test to display
+ */
+ var _printConsole = function(obj) {
+
+ /** colors for console.log %c */
+ var green = "color: green",
+ red = "color: red;",
+ orange = "color: orange",
+ blue = "color: blue",
+ normal = "color: normal; font-weight:normal;";
+
+ /** Try to figure out what type of object display and open group */
+ switch (obj.type) {
+ case 'group' : {
+ /** some difference depends on status */
+ switch (obj.status) {
+ /** if object passed - make collapsed group*/
+ case 'pass' : {
+ console.groupCollapsed("%s - %c%s%c - %c%d%c/%c%d%c/%c%d%c (%c%d%c ms) %s"
+ ,obj.name,green,obj.status,normal
+ ,green,obj.result.pass,normal
+ ,red,obj.result.fail,normal
+ ,orange,obj.result.error,normal
+ ,blue,obj.time,normal,((obj.comment)?obj.comment:''));
+ } break;
+ case 'fail' : {
+ console.group("%s - %c%s%c - %c%d%c/%c%d%c/%c%d%c (%c%d%c ms) %s"
+ ,obj.name,red,obj.status,normal
+ ,green,obj.result.pass,normal
+ ,red,obj.result.fail,normal
+ ,orange,obj.result.error,normal
+ ,blue,obj.time,normal,((obj.comment)?obj.comment:''));
+ } break;
+ case 'error' : {
+ console.group("%s - %c%s%c - %c%d%c/%c%d%c/%c%d%c (%c%d%c ms) %s"
+ ,obj.name,orange,obj.status,normal
+ ,green,obj.result.pass,normal
+ ,red,obj.result.fail,normal
+ ,orange,obj.result.error,normal
+ ,blue,obj.time,normal,((obj.comment)?obj.comment:''));
+ } break;
+ /** if status is not defined - display error; finish displaying */
+ default : {
+ console.error("No status in object %s",obj.name);
+ return false;
+ }
+ }
+
+ /** display description if defined */
+ if (obj.description) {
+ console.log(obj.description);
+ }
+
+ /**
+ * display all tests and groups in stack
+ * It will make new levels of group, if there are groups in stack.
+ */
+ for (var i in obj.stack) {
+ _printConsole(obj.stack[i]);
+ }
+
+ /** display error if defined */
+ if (obj.error) {
+ // console.error(obj.error);
+ console.group('%c%s%c: %s',orange,obj.error.type,normal,obj.error.message);
+ console.log(obj.error.stack);
+ console.dir(obj.error.error);
+ console.groupEnd();
+ }
+
+ /** close opened group (current level) */
+ console.groupEnd();
+
+ } break;
+ case 'test' : {
+ /** display different results, depend on status */
+ switch (obj.status) {
+ case 'pass' : {
+ /** if pass - collaps group*/
+ console.groupCollapsed("%cpass%c: %s",green,normal,(obj.comment)?obj.comment:'');
+ } break;
+ case 'fail' : {
+ console.group("%cfail%c: %s",red,normal,(obj.comment)?obj.comment:'');
+ } break;
+ case 'error' : {
+ console.group("%cerror%c: %s",orange,normal,(obj.comment)?obj.comment:'');
+ } break;
+ }
+ if (obj.description) console.log(obj.description);
+ /** display error if defined */
+ if (obj.error) {
+ // console.error(obj.error);
+ console.group('%c%s%c: %s',orange,obj.error.type,normal,obj.error.message);
+ console.log(obj.error.stack);
+ console.dir(obj.error.error);
+ console.groupEnd();
+ }
+ console.log(obj.argument);
+ console.groupEnd();
+ } break;
+ }
+ }
+ /**
+ * public interface for _printConsole
+ * @type {Function}
+ * @public
+ * @example
+ * test.ptint(test.root);
+ */
+ this.print = _printConsole;
+
+ /**
+ * determinate type of argument
+ * More powerfull then typeof().
+ * @private
+ * @return {String} type name of argument
+ * undefined, if type was not determinated
+ */
+ var _typeof = function (argument) {
+ var type;
+ try {
+ switch (argument.constructor) {
+ case Array : type='Array';break;
+ case Boolean : type='Boolean';break;
+ case Date : type='Date';break;
+ case Error : type='Error';break;
+ case EvalError : type='EvalError';break;
+ case Function : type='Function';break;
+ // case Math : type='math';break;
+ case Number : {type=(isNaN(argument))?'NaN':'Number';}break;
+ case Object : type='Object';break;
+ case RangeError : type='RangeError';break;
+ case ReferenceError : type='ReferenceError';break;
+ case RegExp : type='RegExp';break;
+ case String : type='String';break;
+ case SyntaxError : type='SyntaxError';break;
+ case TypeError : type='TypeError';break;
+ case URIError : type='URIError';break;
+ case Window : type='Window';break;
+ case HTMLDocument : type='HTML';break;
+ case NodeList : type='NodeList';break;
+ default : {
+ if (typeof argument === 'object'
+ && argument.toString().indexOf('HTML') !== -1) {
+ type = 'HTML';
+ } else {
+ type = undefined;
+ }
+ }
+ }
+ } catch (e) {
+ type = (argument === null)? 'null' : typeof argument;
+ }
+ return type;
+ }
+ /**
+ * public interface for _typeof
+ * @public
+ * @example
+ * test.typeof(myVar);
+ */
+ this.typeof = _typeof;
+ /** list of type, which _typeof can identify */
+ var identifiedTypes = ['array', 'boolean', 'date', 'error', 'evalerror', 'function', 'html', 'nan', 'nodelist', 'null', 'number', 'object', 'rangeerror', 'referenceerror', 'regexp', 'string', 'syntaxerror', 'typeerror', 'urierror', 'window'];
+
+ /**
+ * public interface for getTrace(error)
+ * @public
+ * @example
+ * test.trace();
+ */
+ this.trace = getTrace;
+
+
+
+}
+
+/**
+ * figure out what status will be used
+ * Depends on significanse:
+ * More significant -> less significant.
+ * error -> fail -> pass -> undefined
+ * @param {String} oldstatus first compared status
+ * @param {String} newstatus second compared status
+ * @return {String} status which will be set
+ */
+function updateStatus(oldstatus,newstatus) {
+ if (oldstatus===undefined) return newstatus;
+ if (newstatus===undefined) return oldstatus;
+ if (oldstatus==='error' || newstatus==='error') return 'error';
+ if (oldstatus==='fail' || newstatus==='fail') return 'fail';
+ return 'pass';
+}
+
+/**
+ * make more understandable error object
+ * @type {Object}
+ * @param {Error} error basic error
+ * @param {Object} object understandable error object
+ */
+function generateError(error,object) {
+ /**
+ * understandable error object
+ * @property {Error} error consist basic error
+ * @property {String} type type of error
+ * @property {String} message message from basic property
+ * @property {String} stack some kind of result of trace()
+ */
+ object.error = error;
+ object.type = test.typeof(error);
+ object.message = error.message;
+ object.stack = getTrace(error);
+}
+
+/**
+ * returns a list of functions that have been performed to call the current line
+ * @param {Error} error if setted, trace will be based on it stack
+ * @return {String} list of functions joined by "\n";
+ */
+function getTrace(error) {
+ if (!error) error = new Error();
+ var stack = '';
+ ( error.stack ? error.stack : error.message ).split(/[\n]/).forEach(function(i,n){
+ var addToStack = true;
+ /** take off empty strings (FireBug) */
+ if (i==='') addToStack = false;
+ /** take off Errors (Chrome) */
+ if (i.indexOf(test.typeof(error))!==-1) addToStack = false;
+ /** take of reference to this function */
+ if (i.indexOf('getTrace')!==-1) addToStack = false;
+ /** take off any references to testit methods */
+ for (prop in test) {
+ if (i.indexOf('[as '+prop+']')!==-1) addToStack = false;
+ }
+ /** fill the stack */
+ if (addToStack) {
+ stack += (stack)?'\n':'';
+ stack += i.replace(/((\s+at\s+)|(^@))/,'');
+ }
+ })
+ return stack;
+}
+
+/**
+ * Compare any type of variables
+ * @return {Boolean} result of comparison
+ * {@link http://stackoverflow.com/a/1144249/1771942}
+ */
+function deepCompare(){function c(d,e){var f;if(isNaN(d)&&isNaN(e)&&"number"==typeof d&&"number"==typeof e)return!0;if(d===e)return!0;if("function"==typeof d&&"function"==typeof e||d instanceof Date&&e instanceof Date||d instanceof RegExp&&e instanceof RegExp||d instanceof String&&e instanceof String||d instanceof Number&&e instanceof Number)return d.toString()===e.toString();if(!(d instanceof Object&&e instanceof Object))return!1;if(d.isPrototypeOf(e)||e.isPrototypeOf(d))return!1;if(d.constructor!==e.constructor)return!1;if(d.prototype!==e.prototype)return!1;if(a.indexOf(d)>-1||b.indexOf(e)>-1)return!1;for(f in e){if(e.hasOwnProperty(f)!==d.hasOwnProperty(f))return!1;if(typeof e[f]!=typeof d[f])return!1}for(f in d){if(e.hasOwnProperty(f)!==d.hasOwnProperty(f))return!1;if(typeof e[f]!=typeof d[f])return!1;switch(typeof d[f]){case"object":case"function":if(a.push(d),b.push(e),!c(d[f],e[f]))return!1;a.pop(),b.pop();break;default:if(d[f]!==e[f])return!1}}return!0}var a,b;if(arguments.length<1)return!0;for(var d=1,e=arguments.length;e>d;d++)if(a=[],b=[],!c(arguments[0],arguments[d]))return!1;return!0}
+
+/**
+ * find val in array
+ * @param {Array} array will be searched
+ * @param val will be searched for
+ * @return {Boolean} true if found, false otherwise
+ */
+arrayConsist = function(array, val) {
+ for (var i in array) if (array[i] === val) return true;
+ return false;
+}
+/**
+ * make new instance of testit
+ * Make it availible from outside.
+ */
+window.test = new testit();
+
+})(window)
View
1  example/testit.v1.0.2.min.js
@@ -1 +0,0 @@
-!function(a){function d(a,b){b.error=a,b.type=test.typeof(a),b.message=a.message,b.stack=e(a)}function e(a){a||(a=new Error);var b="";return a.stack.split(/[\n]/).forEach(function(c){var e=!0;""===c&&(e=!1),-1!==c.indexOf(test.typeof(a))&&(e=!1),-1!==c.indexOf("getTrace")&&(e=!1);for(prop in test)-1!==c.indexOf("[as "+prop+"]")&&(e=!1);e&&(b+=b?"\n":"",b+=c.replace(/((\s+at\s+)|(^@))/,""))}),b}function f(){function a(d,e){var f;if(isNaN(d)&&isNaN(e)&&"number"==typeof d&&"number"==typeof e)return!0;if(d===e)return!0;if("function"==typeof d&&"function"==typeof e||d instanceof Date&&e instanceof Date||d instanceof RegExp&&e instanceof RegExp||d instanceof String&&e instanceof String||d instanceof Number&&e instanceof Number)return d.toString()===e.toString();if(!(d instanceof Object&&e instanceof Object))return!1;if(d.isPrototypeOf(e)||e.isPrototypeOf(d))return!1;if(d.constructor!==e.constructor)return!1;if(d.prototype!==e.prototype)return!1;if(b.indexOf(d)>-1||c.indexOf(e)>-1)return!1;for(f in e){if(e.hasOwnProperty(f)!==d.hasOwnProperty(f))return!1;if(typeof e[f]!=typeof d[f])return!1}for(f in d){if(e.hasOwnProperty(f)!==d.hasOwnProperty(f))return!1;if(typeof e[f]!=typeof d[f])return!1;switch(typeof d[f]){case"object":case"function":if(b.push(d),c.push(e),!a(d[f],e[f]))return!1;b.pop(),c.pop();break;default:if(d[f]!==e[f])return!1}}return!0}var b,c;if(arguments.length<1)return!0;for(var d=1,e=arguments.length;e>d;d++)if(b=[],c=[],!a(arguments[0],arguments[d]))return!1;return!0}var b=function(){var a=function(){this.type="group",this.name=void 0,this.status=void 0,this.comment=void 0,this.error=void 0,this.time=0,this.result={pass:0,fail:0,error:0,total:0},this.stack=[]},b=function(){this.type="test",this.status=void 0,this.comment=void 0,this.description=void 0,this.error=void 0,this.argument=[]},c=new a;this.root=c,c.name="root",c.time=(new Date).getTime();var g=function(b,e){switch(arguments.length){case 0:throw new RangeError("test.group expect at least 1 argument");case 1:var f=this.link?this.link.stack:c.stack;for(var g in f)if("group"===f[g].type&&f[g].name===b)return Object.create(this,{link:{value:f[g]}});throw new ReferenceError("there are no group with name: "+b);case 2:break;default:throw new RangeError("test.group expect maximum of 2 arguments")}var h=(new Date).getTime(),i=c;c=this.link?this.link:c;var j,k=!1;for(var g in c.stack)if("group"===c.stack[g].type&&c.stack[g].name===b){j=c.stack[g],k=!0;break}k||(j=new a),j.name=b,j.linkBack=c;var l;k&&(l=j.status),j.status="pass";var m=c;c=j;try{e()}catch(n){j.status="error";var o={};d(n,o),j.error=o}return c=m,c=i,j.time+=(new Date).getTime()-h,k||c.stack.push(j),t(j),Object.create(this,{link:{value:j}})};this.group=g;var h=function(a,e){var f=new b;for(var g in arguments)f.argument.push(arguments[g]);switch(arguments.length){case 0:f.status="error";var h=new RangeError("at least one argument expected"),i={};d(h,i),f.error=i;break;case 1:m(f,[a]);break;case 2:n(f,[a,e]);break;default:f.status="error";var h=new RangeError("too much arguments"),i={};d(h,i),f.error=i}return c.stack.push(f),t(c),Object.create(this,{link:{value:f}})};this.it=h;var i=function(a){var e=new b;for(var f in arguments)e.argument.push(arguments[f]);if(0===arguments.length||arguments.length>1){e.status="error";var g=new RangeError("test.them expects exactly 1 argument"),h={};d(g,h),e.error=h}else if("Array"!==v(a)){e.status="error";var g=new TypeError("test.them expects to receive an array"),h={};d(g,h),e.error=h}else m(e,a);return c.stack.push(e),t(c),Object.create(this,{link:{value:e}})};this.them=i;var j=function(a,e){var f=new b;for(var g in arguments)f.argument.push(arguments[g]);if(2!==arguments.length){f.status="error";var h=new RangeError("test.type expect two arguments"),i={};d(h,i),f.error=i}else if("String"!==v(e)){f.status="error";var h=new TypeError("second argument must be a String"),i={};d(h,i),f.error=i}else if(arrayConsist(w,e.toLowerCase()))l(f,[a],e);else{f.status="error";var h=new TypeError("second argument must be a standart type"),i={};d(h,i),f.error=i}return c.stack.push(f),t(c),Object.create(this,{link:{value:f}})};this.type=j;var k=function(a,e){var f=new b;for(var g in arguments)f.argument.push(arguments[g]);if(arguments.length>2){f.status="error";var h=new RangeError("test.types expect maximum of two arguments"),i={};d(h,i),f.error=i}else if("Array"!==v(a)){f.status="error";var h=new TypeError("test.types expect array in first argument"),i={};d(h,i),f.error=i}else if(e)if("String"!==v(e)){f.status="error";var h=new TypeError("second argument must be a String"),i={};d(h,i),f.error=i}else if(arrayConsist(w,e.toLowerCase()))l(f,a,e);else{f.status="error";var h=new TypeError("second argument must be a standart type"),i={};d(h,i),f.error=i}else if(a.length<2){f.status="error";var h=new RangeError("test.types expect array with minimum 2 values, if second argument not defined"),i={};d(h,i),f.error=i}else l(f,a);return c.stack.push(f),t(c),Object.create(this,{link:{value:f}})};this.types=k;var l=function(a,b,c){a.description="type of argument is ",c||(c=v(b[0]));for(arg in b)if(v(b[arg]).toLowerCase()!==c.toLowerCase())return a.description+="not "+c,a.status="fail",void 0;a.description+=c,a.status="pass"},m=function(a,b){a.description=1===b.length?"argument is not ":"arguments is not ";for(arg in b)if(!b[arg])return a.description+="true",a.status="fail",void 0;a.description+="false",a.status="pass"},n=function(a,b){var c=b.shift(),d=v(c);for(arg in b)if(v(b[arg])!==d)return a.status="fail",a.description="arguments has different types",void 0;for(arg in b)if(!f(c,b[arg]))return a.description="arguments are not equal",a.status="fail",void 0;a.description="arguments are equal",a.status="pass"},o=function(a){if(!this.link)throw new ReferenceError("comment can only be used in testit chain");return this.link.comment=a,this};this.comment=o;var p=function(a,b,c){if(!this.link)throw new ReferenceError("callback can only be used in testit chain");if("pass"===this.link.status&&"Function"===v(a))try{a()}catch(d){throw d}if("fail"===this.link.status&&"Function"===v(b))try{b()}catch(d){throw d}if("error"===this.link.status&&"Function"===v(c))try{c()}catch(d){throw d}return this};this.callback=p;var q=function(){return this.link?"pass"==this.link.status?!0:!1:void 0};this.result=q;var r=function(){if(this.link){if("test"!==this.link.type)return TypeError("groups does not return arguments");switch(this.link.argument.length){case 0:return void 0;case 1:return this.link.argument[0];default:return this.link.argument}}return void 0};this.arguments=r;var s=function(){c.time=(new Date).getTime()-c.time,u(c)};this.done=s;var t=function(a){a.result={pass:0,fail:0,error:0,total:0};for(var b in a.stack)switch(a.result.total++,a.stack[b].status){case"pass":a.result.pass++;break;case"fail":a.result.fail++;break;case"error":a.result.error++}a.status=a.result.error||a.error?"error":a.result.fail?"fail":"pass",a.linkBack&&t(a.linkBack)},u=function(a){var b="color: green",c="color: red;",d="color: orange",e="color: blue",f="color: normal; font-weight:normal;";switch(a.type){case"group":switch(a.status){case"pass":console.groupCollapsed("%s - %c%s%c - %c%d%c/%c%d%c/%c%d%c (%c%d%c ms) %s",a.name,b,a.status,f,b,a.result.pass,f,c,a.result.fail,f,d,a.result.error,f,e,a.time,f,a.comment?a.comment:"");break;case"fail":console.group("%s - %c%s%c - %c%d%c/%c%d%c/%c%d%c (%c%d%c ms) %s",a.name,c,a.status,f,b,a.result.pass,f,c,a.result.fail,f,d,a.result.error,f,e,a.time,f,a.comment?a.comment:"");break;case"error":console.group("%s - %c%s%c - %c%d%c/%c%d%c/%c%d%c (%c%d%c ms) %s",a.name,d,a.status,f,b,a.result.pass,f,c,a.result.fail,f,d,a.result.error,f,e,a.time,f,a.comment?a.comment:"");break;default:return console.error("No status in object %s",a.name),!1}a.description&&console.log(a.description);for(var g in a.stack)u(a.stack[g]);a.error&&(console.group("%c%s%c: %s",d,a.error.type,f,a.error.message),console.log(a.error.stack),console.dir(a.error.error),console.groupEnd()),console.groupEnd();break;case"test":switch(a.status){case"pass":console.groupCollapsed("%cpass%c: %s",b,f,a.comment?a.comment:"");break;case"fail":console.group("%cfail%c: %s",c,f,a.comment?a.comment:"");break;case"error":console.group("%cerror%c: %s",d,f,a.comment?a.comment:"")}a.description&&console.log(a.description),a.error&&(console.group("%c%s%c: %s",d,a.error.type,f,a.error.message),console.log(a.error.stack),console.dir(a.error.error),console.groupEnd()),console.log(a.argument),console.groupEnd()}};this.print=u;var v=function(a){var b;try{switch(a.constructor){case Array:b="Array";break;case Boolean:b="Boolean";break;case Date:b="Date";break;case Error:b="Error";break;case EvalError:b="EvalError";break;case Function:b="Function";break;case Number:b=isNaN(a)?"NaN":"Number";break;case Object:b="Object";break;case RangeError:b="RangeError";break;case ReferenceError:b="ReferenceError";break;case RegExp:b="RegExp";break;case String:b="String";break;case SyntaxError:b="SyntaxError";break;case TypeError:b="TypeError";break;case URIError:b="URIError";break;case Window:b="Window";break;case HTMLDocument:b="HTML";break;case NodeList:b="NodeList";break;default:b="object"==typeof a&&-1!==a.toString().indexOf("HTML")?"HTML":void 0}}catch(c){b=null===a?"null":typeof a}return b};this.typeof=v;var w=["array","boolean","date","error","evalerror","function","html","nan","nodelist","null","number","object","rangeerror","referenceerror","regexp","string","syntaxerror","typeerror","urierror","window"];this.trace=e};arrayConsist=function(a,b){for(var c in a)if(a[c]===b)return!0;return!1},a.test=new b}(window);
View
2  testit.js
@@ -906,7 +906,7 @@ function generateError(error,object) {
function getTrace(error) {
if (!error) error = new Error();
var stack = '';
- error.stack.split(/[\n]/).forEach(function(i,n){
+ ( error.stack ? error.stack : error.message ).split(/[\n]/).forEach(function(i,n){
var addToStack = true;
/** take off empty strings (FireBug) */
if (i==='') addToStack = false;
Something went wrong with that request. Please try again.