├── README.md ├── lib └── framework.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # This repo is deprecated!! 2 | Please see the [codewars-cli](http://www.github.com/codewars/codewars-cli) project. The JS test framework is now contained within that project. 3 | 4 | 5 | kata-test-framework-js 6 | ====================== 7 | 8 | The framework used by Codewars for testing kata 9 | 10 | 11 | ### About 12 | This repository has been added so that Codewars users can view the code used for testing JavaScript and CoffeeScript kata. 13 | It is a part of an initiative to open-source a portion of the site. This version of the framework is almost identical to the 14 | one used on the site, with only a few security related lines of code removed. This code is not designed to be used in any other 15 | context other than on Codewars. 16 | 17 | ### Contributions 18 | The purpose of this repo is to allow other developers the opportunity to contribute to the kata framework. Any optimizations or additions are welcome. 19 | 20 | The workflow is: 21 | 22 | - Fork this repo 23 | - Make optimizations or additions to the code base 24 | - Submit pull-request 25 | 26 | Not all pull-requests will be accepted, due mostly to size constrictions. Ideally this framework should be kept as small as possible. Once 27 | a pull-request is accepted it will need to be manually merged into the actual Codewars codebase by someone on the Codewars team. 28 | -------------------------------------------------------------------------------- /lib/framework.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | var methodCalls = {}, 3 | html = [], 4 | consoleLog = console.log, 5 | describing = false, 6 | correct = 0, 7 | incorrect = 0, 8 | failed = [], 9 | beforeCallbacks = [], 10 | afterCallbacks = []; 11 | 12 | var _expect = function(passed, msg, options){ 13 | options = options || {} 14 | 15 | if(passed){ 16 | var successMsg = "Test Passed" 17 | if (options.successMsg){ 18 | successMsg += ": " + options.successMsg 19 | } 20 | write(logFilter('
') + successMsg + 21 | logFilter('
', true), true) 22 | correct++ 23 | } else { 24 | msg = _message(msg) || 'Invalid' 25 | // extra credit feature - TODO: complete 26 | if (options.extraCredit) { 27 | msg = (options.extraCredit !== true) ? _message(options.extraCredit) : null 28 | msg = combineMessages(["Test Missed", msg], ": "); 29 | write(logFilter("
") + msg + 30 | logFilter("
", true), true) 31 | incorrect++ 32 | } 33 | else{ 34 | write(logFilter("
Test Failed: ") + msg + 35 | logFilter("
", true), true) 36 | var error = new Test.Error(msg); 37 | if (describing){ 38 | failed.push(error); 39 | } 40 | else{ 41 | throw error; 42 | } 43 | } 44 | } 45 | } 46 | 47 | function logFilter( msg, lf ){ 48 | // detect node.js 49 | if ( this.exports ){ 50 | return msg; 51 | } else { 52 | var m = msg.replace( /<[^>].*>/g, ''); 53 | // Add a LF only for empty messages that don't have the *lf* set 54 | return ( m.length == 0 && ( ! lf ) ) ? "" : m + "\n"; 55 | } 56 | } 57 | 58 | 59 | function write(msg, noLineBreak){ 60 | if ((msg.length == 0) && (! this.exports)){ 61 | return 62 | } 63 | 64 | if (html.length == 0){ 65 | console.log(msg); 66 | } 67 | else{ 68 | html.push(msg); 69 | if(!noLineBreak){ 70 | html.push(logFilter('
')); 71 | } 72 | } 73 | } 74 | 75 | function combineMessages(msgs, separator){ 76 | return msgs.filter(function(m){return m != null;}).join(separator) 77 | } 78 | 79 | function _message(msg, prefix){ 80 | 81 | if (typeof msg == 'function'){ 82 | msg = msg() 83 | }else if (typeof msg == 'array'){ 84 | msg = combineMessages(msg, ' - ') 85 | } 86 | return prefix ? (prefix + ' - ' + msg) : msg 87 | } 88 | 89 | function logCall(name, useConsole){ 90 | methodCalls[name] = Test.callCount(name) + 1 91 | 92 | if (useConsole){ 93 | console.log(name + " called"); 94 | } 95 | } 96 | 97 | var Test = { 98 | callCount: function(name) { 99 | return methodCalls[name] || 0 100 | }, 101 | inspect: function(obj){ 102 | logCall('inspect') 103 | if(typeof obj == 'string'){ 104 | return obj; 105 | } else { 106 | return obj && obj !== true ? JSON.stringify(obj) : ('' + obj) 107 | } 108 | }, 109 | describe: function(msg, fn) { 110 | try{ 111 | if (describing) throw "cannot call describe within another describe" 112 | logCall('describe') 113 | describing = true 114 | html.push(logFilter('
')); 115 | html.push(_message(msg)) 116 | html.push(logFilter(':
')); 117 | // intercept console.log messages 118 | console.log = write 119 | fn(); 120 | } 121 | finally{ 122 | html.push(logFilter('
')); 123 | // restore log 124 | console.log = consoleLog 125 | console.log(html.join('')); 126 | html = [] 127 | describing = false 128 | beforeCallbacks = []; 129 | afterCallbacks = []; 130 | if (failed.length > 0){ 131 | throw failed[0]; 132 | } 133 | } 134 | }, 135 | it: function(msg, fn) { 136 | try{ 137 | logCall('it'); 138 | html.push(logFilter('
')) 139 | html.push(_message(msg)); 140 | html.push(logFilter(':
')); 141 | beforeCallbacks.forEach(function(cb){ 142 | cb() 143 | }); 144 | try{ 145 | fn(); 146 | } finally { 147 | afterCallbacks.forEach(function(cb){ 148 | cb() 149 | }); 150 | } 151 | } 152 | finally{ 153 | html.push(logFilter('
')); 154 | } 155 | }, 156 | before: function(cb) { 157 | beforeCallbacks.push(cb); 158 | }, 159 | after: function(cb) { 160 | afterCallbacks.push(cb); 161 | }, 162 | expect: function(passed, message, options){ 163 | logCall('expect') 164 | _expect(passed, message, options) 165 | }, 166 | assertSimilar: function(actual, expected, msg, options){ 167 | logCall('assertSimilar') 168 | this.assertEquals(this.inspect(actual), this.inspect(expected), msg, options) 169 | }, 170 | assertNotSimilar: function(actual, expected, msg, options){ 171 | logCall('assertNotSimilar') 172 | this.assertNotEquals(this.inspect(actual), this.inspect(expected), msg, options) 173 | }, 174 | assertEquals: function(actual, expected, msg, options) { 175 | logCall('assertEquals') 176 | if(actual !== expected){ 177 | msg = _message('Expected: ' + Test.inspect(expected) + ', instead got: ' + Test.inspect(actual), msg) 178 | Test.expect(false, msg, options); 179 | }else{ 180 | options = options || {} 181 | options.successMsg = options.successMsg || 'Value == ' + Test.inspect(expected) 182 | Test.expect(true, null, options) 183 | } 184 | }, 185 | assertNotEquals: function(a, b, msg, options){ 186 | logCall('assertNotEquals') 187 | if(a === b){ 188 | msg = _message('Not Expected: ' + Test.inspect(a), msg) 189 | Test.expect(false, msg, options) 190 | }else{ 191 | options = options || {} 192 | options.successMsg = options.successMsg || 'Value != ' + Test.inspect(b) 193 | Test.expect(true, null, options) 194 | } 195 | }, 196 | expectNoError: function(msg, fn){ 197 | logCall('expectNoError') 198 | if(!fn){ 199 | fn = msg; 200 | msg = 'Unexpected error was raised' 201 | } 202 | 203 | try{ 204 | fn(); 205 | Test.expect(true) 206 | }catch(ex){ 207 | if (ex.name == 'Test:Error'){ 208 | throw ex; 209 | } 210 | else { 211 | msg += ': ' + ex.toString() 212 | Test.expect(false, msg) 213 | } 214 | } 215 | }, 216 | expectError: function(msg, fn, options){ 217 | logCall('expectError') 218 | if(!fn){ 219 | fn = msg; 220 | msg = 'Unexpected error was raised.' 221 | } 222 | 223 | var passed = false 224 | try{ 225 | fn(); 226 | }catch(ex){ 227 | console.log(logFilter('Expected error was thrown: ') + ex.toString()) 228 | passed = true 229 | } 230 | 231 | Test.expect(passed, msg, options) 232 | }, 233 | randomNumber: function(){ 234 | logCall('randomNumber'); 235 | return Math.round(Math.random() * 100) 236 | }, 237 | randomToken: function(){ 238 | return Math.random().toString(36).substr(8) 239 | }, 240 | randomize: function(array){ 241 | logCall('randomize'); 242 | var arr = array.concat(), i = arr.length, j, x; 243 | while(i) { 244 | j = (Math.random() * i) | 0; 245 | x = arr[--i]; 246 | arr[i] = arr[j]; 247 | arr[j] = x; 248 | } 249 | return arr; 250 | }, 251 | sample: function(array){ 252 | logCall('sample'); 253 | return array[~~(array.length * Math.random())] 254 | }, 255 | Error: function(message){ 256 | logCall('Error'); 257 | this.name = "Test:Error"; 258 | this.message = (message || ""); 259 | } 260 | } 261 | 262 | Test.Error.prototype = Error.prototype; 263 | 264 | Test.inspect.toString = function(){} 265 | Test.randomize.toString = function(){} 266 | Test.sample.toString = function(){} 267 | Test.randomNumber.toString = function(){} 268 | 269 | Object.freeze(Test) 270 | 271 | exports.Test = Test; 272 | exports.describe = Test.describe; 273 | exports.it = Test.it; 274 | exports.before = Test.before; 275 | exports.after = Test.after 276 | 277 | 278 | })(typeof module != 'undefined' ? module.exports : this) 279 | 280 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kata-test-framework-js", 3 | "description": "Kata test framework used by Codewars", 4 | "directories": { 5 | "lib": "./lib" 6 | }, 7 | "main": "./lib/framework", 8 | "version": "0.0.1", 9 | "dependencies": {}, 10 | "engine": "node >= 0.8.6" 11 | } 12 | 13 | --------------------------------------------------------------------------------