├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── app ├── .eslintrc.js ├── arrays.js ├── async.js ├── bestPractices.js ├── count.js ├── flowControl.js ├── functions.js ├── logicalOperators.js ├── modules.js ├── numbers.js ├── objects.js ├── recursion.js ├── regex.js └── strings.js ├── data └── testdata.json ├── help.txt ├── index.js ├── lib └── plugins │ ├── text.js │ └── use.js ├── package.json └── tests ├── app ├── .eslintrc.js ├── arrays.js ├── async.js ├── bestPractices.js ├── count.js ├── flowControl.js ├── functions.js ├── logicalOperators.js ├── modules.js ├── numbers.js ├── objects.js ├── recursion.js ├── regex.js └── strings.js └── runner.html /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: "4" 3 | script: "npm run lint" 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 0.3.0 4 | 5 | - Remove jquery, backbone, underscore, mocha, and chai as local libraries 6 | - Remove RequireJS 7 | - node deps use native `require()` 8 | - browser deps are `script` tags 9 | 10 | ## 0.2.0 11 | 12 | - Convert app from Express to using serve-static 13 | - Replace bin/server with a `grunt connect` task and `npm start` script 14 | - Update dependencies 15 | 16 | ## < 0.1.0 17 | 18 | - Initial commits. Hello World! 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/rmurphey/js-assessment.svg?branch=master)](https://travis-ci.org/rmurphey/js-assessment) 2 | 3 | # A test-driven JS assessment 4 | 5 | This repo includes a set of tests that can be used to assess the skills of 6 | a candidate for a JavaScript position, or to evaluate and improve one's own 7 | skills. 8 | 9 | ## I want to work on the tests; what do I do? 10 | To use the tests, you will need to install [Node](https://nodejs.org/). Note 11 | that on Windows, there are some reports that you will need to restart 12 | after installing Node - see #12. 13 | 14 | You can clone or download this repo. Once you have done so, from the root 15 | directory of the repo, run: 16 | 17 | npm install 18 | npm start 19 | 20 | You can then view the tests in your browser at 21 | [http://localhost:4444](http://localhost:4444). 22 | 23 | When you visit that page, all of the tests should be failing; your job is to 24 | get the tests to pass. To do this, you'll need to refer to the tests in the 25 | files in the `tests/app` directory, and edit the files in the `app/` directory. 26 | Once you update a test, you can reload the test page in the browser to see 27 | whether it worked. 28 | 29 | You can also run (most of) the tests on the command line: 30 | 31 | npm test 32 | 33 | The command line runner is a work in progress; contributions welcome :) 34 | 35 | ### Available dependencies 36 | 37 | The repo includes jQuery, Backbone, and Underscore. You can use these 38 | libraries when writing your solutions! 39 | 40 | ## I want to contribute tests; what do I do? 41 | 42 | Submit a pull request! The tests are currently loosely organized by topic, so 43 | you should do your best to add tests to the appropriate file in `tests/app`, or 44 | create a new file there if you don't see an appropriate one. If you do create 45 | a new file, make sure to add it to `tests/runner.js`, and to add a stub for the 46 | solution to the corresponding file in `app/`. Finally, it would be great if you 47 | could update the [answers](https://github.com/rmurphey/js-assessment-answers) 48 | as well. 49 | 50 | If you're not sure how or where to add a test, please open an issue. 51 | 52 | ### Data-driven tests 53 | 54 | If your tests need data that can be fetched via XHR, stick a `.json` file in 55 | the `data` directory; you can access it at `/data/.json`. 56 | 57 | ## I want to see the answers! 58 | 59 | First, bear in mind that looking up the answers is going to teach you a whole 60 | lot less than you'll learn by working on the tests, even if you occasionally get 61 | stuck. I'd recommend only looking at the answers once you have the tests 62 | passing, to see if there's another way you could have approached the 63 | problem. When you're ready to look at the answers, you can find them 64 | [here](https://github.com/rmurphey/js-assessment-answers); I'll do my best to 65 | keep them up to date. 66 | 67 | ## I hate \ 68 | 69 | This repo uses [Mocha](https://github.com/mochajs/mocha) and 70 | [Chai](http://chaijs.com/) for the tests themselves. It uses the BDD style for authoring tests. 71 | If this doesn't suit you, please fork away, or, better, submit a pull request that lets 72 | this be more flexible than it currently is. 73 | 74 | # Todos 75 | 76 | There are a number of things that would make this project better; check out the 77 | [issues](https://github.com/rmurphey/js-assessment/issues) for details, pull 78 | requests welcome! 79 | 80 | # License 81 | 82 | Copyright © 2012-2016 Rebecca Murphey with many thanks to several 83 | [contributors](https://github.com/rmurphey/js-assessment/graphs/contributors). 84 | 85 | Creative Commons License 86 | 87 | This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. 88 | -------------------------------------------------------------------------------- /app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "node": true 5 | }, 6 | "globals": { 7 | "exports": false, 8 | }, 9 | "extends": "eslint:recommended", 10 | "rules": { 11 | "accessor-pairs": "error", 12 | "array-callback-return": "error", 13 | "arrow-body-style": "error", 14 | "arrow-parens": "error", 15 | "arrow-spacing": "error", 16 | "block-spacing": [ 17 | "error", 18 | "always" 19 | ], 20 | "callback-return": "error", 21 | "camelcase": "error", 22 | "comma-spacing": [ 23 | "error", 24 | { 25 | "after": true, 26 | "before": false 27 | } 28 | ], 29 | "comma-style": [ 30 | "error", 31 | "last" 32 | ], 33 | "complexity": "error", 34 | "computed-property-spacing": [ 35 | "error", 36 | "never" 37 | ], 38 | "consistent-return": "error", 39 | "consistent-this": "error", 40 | "curly": "error", 41 | "default-case": "error", 42 | "dot-location": "error", 43 | "dot-notation": "error", 44 | "eol-last": "error", 45 | "eqeqeq": "error", 46 | "generator-star-spacing": "error", 47 | "guard-for-in": "error", 48 | "handle-callback-err": "error", 49 | "id-blacklist": "error", 50 | "id-match": "error", 51 | "indent": [ "error", 2 ], 52 | "jsx-quotes": "error", 53 | "key-spacing": [ 54 | "error", { 55 | "beforeColon": false, 56 | "afterColon": true 57 | } 58 | ], 59 | "keyword-spacing": "error", 60 | "linebreak-style": [ 61 | "error", 62 | "unix" 63 | ], 64 | "lines-around-comment": "error", 65 | "max-depth": "error", 66 | "max-len": [ "error", 120 ], 67 | "max-nested-callbacks": "error", 68 | "max-params": "error", 69 | "new-cap": "error", 70 | "new-parens": "error", 71 | "newline-per-chained-call": "error", 72 | "no-alert": "error", 73 | "no-array-constructor": "error", 74 | "no-bitwise": "error", 75 | "no-caller": "error", 76 | "no-catch-shadow": "error", 77 | "no-confusing-arrow": "error", 78 | "no-continue": "error", 79 | "no-div-regex": "error", 80 | "no-duplicate-imports": "error", 81 | "no-else-return": "error", 82 | "no-empty-function": "off", 83 | "no-eq-null": "error", 84 | "no-eval": "error", 85 | "no-extend-native": "error", 86 | "no-extra-bind": "error", 87 | "no-extra-label": "error", 88 | "no-extra-parens": "error", 89 | "no-floating-decimal": "error", 90 | "no-implicit-coercion": "error", 91 | "no-implicit-globals": "warn", 92 | "no-implied-eval": "error", 93 | "no-inner-declarations": [ 94 | "error", 95 | "functions" 96 | ], 97 | "no-invalid-this": "error", 98 | "no-iterator": "error", 99 | "no-label-var": "error", 100 | "no-labels": "error", 101 | "no-lone-blocks": "error", 102 | "no-lonely-if": "error", 103 | "no-loop-func": "error", 104 | "no-mixed-requires": "error", 105 | "no-multi-spaces": "error", 106 | "no-multi-str": "error", 107 | "no-multiple-empty-lines": "error", 108 | "no-negated-condition": "error", 109 | "no-nested-ternary": "error", 110 | "no-new": "error", 111 | "no-new-func": "error", 112 | "no-new-object": "error", 113 | "no-new-require": "error", 114 | "no-new-wrappers": "error", 115 | "no-octal-escape": "error", 116 | "no-param-reassign": "error", 117 | "no-path-concat": "error", 118 | "no-process-env": "error", 119 | "no-process-exit": "error", 120 | "no-proto": "error", 121 | "no-restricted-globals": "error", 122 | "no-restricted-imports": "error", 123 | "no-restricted-modules": "error", 124 | "no-restricted-syntax": "error", 125 | "no-return-assign": "error", 126 | "no-script-url": "error", 127 | "no-self-compare": "error", 128 | "no-sequences": "error", 129 | "no-shadow": "error", 130 | "no-shadow-restricted-names": "error", 131 | "no-spaced-func": "error", 132 | "no-sync": "error", 133 | "no-throw-literal": "error", 134 | "no-trailing-spaces": "error", 135 | "no-undef-init": "error", 136 | "no-undefined": "error", 137 | "no-underscore-dangle": "error", 138 | "no-unmodified-loop-condition": "error", 139 | "no-unneeded-ternary": "error", 140 | "no-unused-vars": "off", 141 | "no-use-before-define": "error", 142 | "no-useless-call": "error", 143 | "no-useless-concat": "error", 144 | "no-useless-constructor": "error", 145 | "no-useless-escape": "error", 146 | "no-void": "error", 147 | "no-whitespace-before-property": "error", 148 | "no-with": "error", 149 | "object-curly-spacing": "error", 150 | "one-var-declaration-per-line": "error", 151 | "operator-assignment": "error", 152 | "operator-linebreak": "error", 153 | "prefer-const": "error", 154 | "prefer-reflect": "error", 155 | "prefer-spread": "error", 156 | "quotes": [ 157 | "error", 158 | "single" 159 | ], 160 | "radix": "error", 161 | "semi": "error", 162 | "semi-spacing": [ 163 | "error", 164 | { 165 | "after": true, 166 | "before": false 167 | } 168 | ], 169 | "sort-imports": "error", 170 | "sort-vars": "error", 171 | "space-before-blocks": "off", 172 | "space-before-function-paren": "off", 173 | "space-in-parens": "off", 174 | "space-infix-ops": "error", 175 | "space-unary-ops": "error", 176 | "spaced-comment": [ 177 | "error", 178 | "always" 179 | ], 180 | "strict": [ 181 | "error", 182 | "never" 183 | ], 184 | "template-curly-spacing": "error", 185 | "valid-jsdoc": "error", 186 | "wrap-iife": "error", 187 | "wrap-regex": "error", 188 | "yield-star-spacing": "error", 189 | "yoda": [ 190 | "error", 191 | "never" 192 | ] 193 | } 194 | }; 195 | -------------------------------------------------------------------------------- /app/arrays.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.arraysAnswers = { 4 | indexOf: function(arr, item) { 5 | 6 | }, 7 | 8 | sum: function(arr) { 9 | 10 | }, 11 | 12 | remove: function(arr, item) { 13 | 14 | }, 15 | 16 | removeWithoutCopy: function(arr, item) { 17 | 18 | }, 19 | 20 | append: function(arr, item) { 21 | 22 | }, 23 | 24 | truncate: function(arr) { 25 | 26 | }, 27 | 28 | prepend: function(arr, item) { 29 | 30 | }, 31 | 32 | curtail: function(arr) { 33 | 34 | }, 35 | 36 | concat: function(arr1, arr2) { 37 | 38 | }, 39 | 40 | insert: function(arr, item, index) { 41 | 42 | }, 43 | 44 | count: function(arr, item) { 45 | 46 | }, 47 | 48 | duplicates: function(arr) { 49 | 50 | }, 51 | 52 | square: function(arr) { 53 | 54 | }, 55 | 56 | findAllOccurrences: function(arr, target) { 57 | 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /app/async.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.asyncAnswers = { 4 | async: function(value) { 5 | 6 | }, 7 | 8 | manipulateRemoteData: function(url) { 9 | 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /app/bestPractices.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | exports = typeof window === 'undefined' ? global : window; 3 | 4 | /** 5 | * This file defines an object with some methods. Some of these methods are 6 | * populated incorrectly; your job is to fix them. Other methods are not 7 | * populated at all; your job is to fill them out. 8 | */ 9 | 10 | exports.bestPracticesAnswers = { 11 | globals: function() { 12 | myObject = { 13 | name: 'Jory' 14 | }; 15 | 16 | return myObject; 17 | }, 18 | 19 | parseInt: function(num) { 20 | return parseInt(num); 21 | }, 22 | 23 | identity: function(val1, val2) { 24 | 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /app/count.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.countAnswers = { 4 | count: function (start, end) { 5 | 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /app/flowControl.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.flowControlAnswers = { 4 | fizzBuzz: function(num) { 5 | // write a function that receives a number as its argument; 6 | // if the number is divisible by 3, the function should return 'fizz'; 7 | // if the number is divisible by 5, the function should return 'buzz'; 8 | // if the number is divisible by 3 and 5, the function should return 9 | // 'fizzbuzz'; 10 | // 11 | // otherwise the function should return the number, or false if no number 12 | // was provided or the value provided is not a number 13 | 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /app/functions.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.functionsAnswers = { 4 | argsAsArray: function(fn, arr) { 5 | 6 | }, 7 | 8 | speak: function(fn, obj) { 9 | 10 | }, 11 | 12 | functionFunction: function(str) { 13 | 14 | }, 15 | 16 | makeClosures: function(arr, fn) { 17 | 18 | }, 19 | 20 | partial: function(fn, str1, str2) { 21 | 22 | }, 23 | 24 | useArguments: function() { 25 | 26 | }, 27 | 28 | callIt: function(fn) { 29 | 30 | }, 31 | 32 | partialUsingArguments: function(fn) { 33 | 34 | }, 35 | 36 | curryIt: function(fn) { 37 | 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /app/logicalOperators.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.logicalOperatorsAnswers = { 4 | or: function(a, b) { 5 | 6 | }, 7 | 8 | and: function(a, b) { 9 | 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /app/modules.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.modulesAnswers = { 4 | createModule: function(str1, str2) { 5 | 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /app/numbers.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.numbersAnswers = { 4 | valueAtBit: function(num, bit) { 5 | 6 | }, 7 | 8 | base10: function(str) { 9 | 10 | }, 11 | 12 | convertToBinary: function(num) { 13 | 14 | }, 15 | 16 | multiply: function(a, b) { 17 | 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /app/objects.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.objectsAnswers = { 4 | alterContext: function(fn, obj) { 5 | 6 | }, 7 | 8 | alterObjects: function(constructor, greeting) { 9 | 10 | }, 11 | 12 | iterate: function(obj) { 13 | 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /app/recursion.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.recursionAnswers = { 4 | listFiles: function(data, dirName) { 5 | 6 | }, 7 | 8 | permute: function(arr) { 9 | 10 | }, 11 | 12 | fibonacci: function(n) { 13 | 14 | }, 15 | 16 | validParentheses: function(n) { 17 | 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /app/regex.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.regexAnswers = { 4 | containsNumber: function(str) { 5 | 6 | }, 7 | 8 | containsRepeatingLetter: function(str) { 9 | 10 | }, 11 | 12 | endsWithVowel: function(str) { 13 | 14 | }, 15 | 16 | captureThreeNumbers: function(str) { 17 | 18 | }, 19 | 20 | matchesPattern: function(str) { 21 | 22 | }, 23 | 24 | isUSD: function(str) { 25 | 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /app/strings.js: -------------------------------------------------------------------------------- 1 | exports = typeof window === 'undefined' ? global : window; 2 | 3 | exports.stringsAnswers = { 4 | reduceString: function(str, amount) { 5 | 6 | }, 7 | 8 | wordWrap: function(str, cols) { 9 | 10 | }, 11 | 12 | reverseString: function(str) { 13 | 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /data/testdata.json: -------------------------------------------------------------------------------- 1 | { 2 | "people" : [ 3 | { "name" : "Matt" }, 4 | { "name" : "Rebecca" }, 5 | { "name" : "Paul" }, 6 | { "name" : "Alex" }, 7 | { "name" : "Adam" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /help.txt: -------------------------------------------------------------------------------- 1 | To work on the tests, open a browser and visit http://{{host}}:{{port}}. 2 | 3 | The tests are in files in the tests/app directory; you will need to fill out 4 | the answers in the corresponding files in the app directory, your browser will 5 | reload when you save the files. 6 | 7 | For some tests, the instructions will be more involved; in those cases, the 8 | inline comments should give you the details you need. 9 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const browserSync = require('browser-sync').create(); 3 | 4 | const port = process.env.PORT || '4444'; 5 | const host = process.env.HOST || '127.0.0.1'; 6 | 7 | browserSync.init({ 8 | server: { 9 | baseDir: __dirname, 10 | index: 'tests/runner.html' 11 | }, 12 | files: ['app/**/*.js'], 13 | host: host, 14 | port: port, 15 | open: false, 16 | notify: false, 17 | ui: false, 18 | ghostMode: false, 19 | logLevel: 'silent' 20 | }); 21 | 22 | f = fs.readFileSync(__dirname + '/help.txt', 'utf8'); 23 | console.log(f.replace('{{host}}', host).replace('{{port}}', port)); 24 | console.log('Server running http://%s:%d', host, port); 25 | -------------------------------------------------------------------------------- /lib/plugins/text.js: -------------------------------------------------------------------------------- 1 | /* 2 | RequireJS text 1.0.7 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. 3 | Available via the MIT or new BSD license. 4 | see: http://github.com/jrburke/requirejs for details 5 | */ 6 | (function(){var k=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"],n=/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,o=/]*>\s*([\s\S]+)\s*<\/body>/im,i=typeof location!=="undefined"&&location.href,p=i&&location.protocol&&location.protocol.replace(/\:/,""),q=i&&location.hostname,r=i&&(location.port||void 0),j=[];define(function(){var g,h,l;typeof window!=="undefined"&&window.navigator&&window.document?h=function(a,c){var b=g.createXhr();b.open("GET",a,!0);b.onreadystatechange= 7 | function(){b.readyState===4&&c(b.responseText)};b.send(null)}:typeof process!=="undefined"&&process.versions&&process.versions.node?(l=require.nodeRequire("fs"),h=function(a,c){var b=l.readFileSync(a,"utf8");b.indexOf("\ufeff")===0&&(b=b.substring(1));c(b)}):typeof Packages!=="undefined"&&(h=function(a,c){var b=new java.io.File(a),e=java.lang.System.getProperty("line.separator"),b=new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(b),"utf-8")),d,f,g="";try{d=new java.lang.StringBuffer; 8 | (f=b.readLine())&&f.length()&&f.charAt(0)===65279&&(f=f.substring(1));for(d.append(f);(f=b.readLine())!==null;)d.append(e),d.append(f);g=String(d.toString())}finally{b.close()}c(g)});return g={version:"1.0.7",strip:function(a){if(a){var a=a.replace(n,""),c=a.match(o);c&&(a=c[1])}else a="";return a},jsEscape:function(a){return a.replace(/(['\\])/g,"\\$1").replace(/[\f]/g,"\\f").replace(/[\b]/g,"\\b").replace(/[\n]/g,"\\n").replace(/[\t]/g,"\\t").replace(/[\r]/g,"\\r")},createXhr:function(){var a,c, 9 | b;if(typeof XMLHttpRequest!=="undefined")return new XMLHttpRequest;else for(c=0;c<3;c++){b=k[c];try{a=new ActiveXObject(b)}catch(e){}if(a){k=[b];break}}if(!a)throw Error("createXhr(): XMLHttpRequest not available");return a},get:h,parseName:function(a){var c=!1,b=a.indexOf("."),e=a.substring(0,b),a=a.substring(b+1,a.length),b=a.indexOf("!");b!==-1&&(c=a.substring(b+1,a.length),c=c==="strip",a=a.substring(0,b));return{moduleName:e,ext:a,strip:c}},xdRegExp:/^((\w+)\:)?\/\/([^\/\\]+)/,useXhr:function(a, 10 | c,b,e){var d=g.xdRegExp.exec(a),f;if(!d)return!0;a=d[2];d=d[3];d=d.split(":");f=d[1];d=d[0];return(!a||a===c)&&(!d||d===b)&&(!f&&!d||f===e)},finishLoad:function(a,c,b,e,d){b=c?g.strip(b):b;d.isBuild&&(j[a]=b);e(b)},load:function(a,c,b,e){if(e.isBuild&&!e.inlineText)b();else{var d=g.parseName(a),f=d.moduleName+"."+d.ext,m=c.toUrl(f),h=e&&e.text&&e.text.useXhr||g.useXhr;!i||h(m,p,q,r)?g.get(m,function(c){g.finishLoad(a,d.strip,c,b,e)}):c([f],function(a){g.finishLoad(d.moduleName+"."+d.ext,d.strip,a, 11 | b,e)})}},write:function(a,c,b){if(c in j){var e=g.jsEscape(j[c]);b.asModule(a+"!"+c,"define(function () { return '"+e+"';});\n")}},writeFile:function(a,c,b,e,d){var c=g.parseName(c),f=c.moduleName+"."+c.ext,h=b.toUrl(c.moduleName+"."+c.ext)+".js";g.load(f,b,function(){var b=function(a){return e(h,a)};b.asModule=function(a,b){return e.asModule(a,h,b)};g.write(a,f,b,d)},d)}}})})(); -------------------------------------------------------------------------------- /lib/plugins/use.js: -------------------------------------------------------------------------------- 1 | if (typeof define !== 'function') { var define = require('amdefine')(module); } 2 | 3 | /* RequireJS Use Plugin v0.2.0 4 | * Copyright 2012, Tim Branyen (@tbranyen) 5 | * use.js may be freely distributed under the MIT license. 6 | */ 7 | (function() { 8 | 9 | // Cache used to map configuration options between load and write. 10 | var buildMap = {}; 11 | 12 | define({ 13 | version: "0.2.0", 14 | 15 | // Invoked by the AMD builder, passed the path to resolve, the require 16 | // function, done callback, and the configuration options. 17 | load: function(name, req, load, config) { 18 | // Dojo provides access to the config object through the req function. 19 | if (!config) { 20 | config = require.rawConfig; 21 | } 22 | 23 | var module = config.use && config.use[name]; 24 | 25 | // No module to load, throw. 26 | if (!module) { 27 | throw new TypeError("Module '" + name + "' is undefined or does not" + 28 | " have a `use` config. Make sure it exists, add a `use` config, or" + 29 | " don't use use! on it"); 30 | } 31 | 32 | // Attach to the build map for use in the write method below. 33 | buildMap[name] = { deps: module.deps || [], attach: module.attach }; 34 | 35 | // Read the current module configuration for any dependencies that are 36 | // required to run this particular non-AMD module. 37 | req(module.deps || [], function() { 38 | var depArgs = arguments; 39 | // Require this module 40 | req([name], function() { 41 | // Attach property 42 | var attach = module.attach; 43 | 44 | // If doing a build don't care about loading 45 | if (config.isBuild) { 46 | return load(); 47 | } 48 | 49 | // Return the correct attached object 50 | if (typeof attach === "function") { 51 | return load(attach.apply(this, depArgs)); 52 | } 53 | 54 | // Use window for now (maybe this?) 55 | return load(this[attach]); 56 | }); 57 | }); 58 | }, 59 | 60 | // Also invoked by the AMD builder, this writes out a compatible define 61 | // call that will work with loaders such as almond.js that cannot read 62 | // the configuration data. 63 | write: function(pluginName, moduleName, write) { 64 | var module = buildMap[moduleName]; 65 | var deps = module.deps; 66 | var normalize = { attach: null, deps: "" }; 67 | 68 | // Normalize the attach to window[name] or function() { } 69 | if (typeof module.attach === "function") { 70 | normalize.attach = module.attach.toString(); 71 | } else { 72 | normalize.attach = [ 73 | "function() {", 74 | "return typeof ", module.attach, 75 | " !== \"undefined\" ? ", module.attach, " : void 0;", 76 | "}" 77 | ].join(""); 78 | } 79 | 80 | // Normalize the dependencies to have proper string characters 81 | if (deps.length) { 82 | normalize.deps = "'" + deps.toString().split(",").join("','") + "'"; 83 | } 84 | 85 | // Write out the actual definition 86 | write([ 87 | "define('", pluginName, "!", moduleName, "', ", 88 | "[", normalize.deps, "], ", normalize.attach, 89 | ");\n" 90 | ].join("")); 91 | } 92 | }); 93 | 94 | })(); 95 | 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Rebecca Murphey (http://rmurphey.com)", 3 | "name": "js-assessment", 4 | "description": "A test-driven assessment of JavaScript skills", 5 | "version": "0.3.0", 6 | "homepage": "http://rmurphey.com", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/rmurphey/js-assessment.git" 10 | }, 11 | "engines": { 12 | "node": "0.12.x" 13 | }, 14 | "dependencies": { 15 | "backbone": "^1.1.2", 16 | "browser-sync": "^2.8.2", 17 | "chai": "^2.3.0", 18 | "expect.js": "0.1.2", 19 | "jquery": "^1.11.0", 20 | "mocha": "^2.2.4", 21 | "sinon": "^1.14.1", 22 | "underscore": "1.8.x" 23 | }, 24 | "scripts": { 25 | "start": "node index.js", 26 | "test": "mocha -R spec 'tests/app'", 27 | "lint": "eslint --quiet tests/app app", 28 | "lint-warn": "eslint tests/app app" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/rmurphey/js-assessment/issues" 32 | }, 33 | "private": true, 34 | "devDependencies": { 35 | "eslint": "^2.8.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true 4 | }, 5 | "globals": { 6 | "arraysAnswers": false, 7 | "asyncAnswers": false, 8 | "bestPracticesAnswers": false, 9 | "countAnswers": false, 10 | "flowControlAnswers": false, 11 | "functionsAnswers": false, 12 | "logicalOperatorsAnswers": false, 13 | "modulesAnswers": false, 14 | "numbersAnswers": false, 15 | "objectsAnswers": false, 16 | "recursionAnswers": false, 17 | "regexAnswers": false, 18 | "stringsAnswers": false, 19 | 20 | "it": false, 21 | "require": false, 22 | "describe": false, 23 | "beforeEach": false, 24 | "afterEach": false, 25 | "before": false, 26 | "after": false 27 | }, 28 | "extends": "eslint:recommended", 29 | "rules": { 30 | "accessor-pairs": "error", 31 | "array-callback-return": "error", 32 | "arrow-body-style": "error", 33 | "arrow-parens": "error", 34 | "arrow-spacing": "error", 35 | "block-spacing": [ 36 | "error", 37 | "always" 38 | ], 39 | "callback-return": "error", 40 | "camelcase": "error", 41 | "comma-spacing": [ 42 | "error", 43 | { 44 | "after": true, 45 | "before": false 46 | } 47 | ], 48 | "comma-style": [ 49 | "error", 50 | "last" 51 | ], 52 | "complexity": "error", 53 | "computed-property-spacing": [ 54 | "error", 55 | "never" 56 | ], 57 | "consistent-return": "error", 58 | "consistent-this": "error", 59 | "curly": "error", 60 | "default-case": "error", 61 | "dot-location": "error", 62 | "dot-notation": "error", 63 | "eol-last": "error", 64 | "eqeqeq": "error", 65 | "generator-star-spacing": "error", 66 | "guard-for-in": "error", 67 | "handle-callback-err": "error", 68 | "id-blacklist": "error", 69 | "id-match": "error", 70 | "indent": [ "error", 2 ], 71 | "jsx-quotes": "error", 72 | "key-spacing": [ 73 | "error", { 74 | "beforeColon": false, 75 | "afterColon": true 76 | } 77 | ], 78 | "keyword-spacing": "error", 79 | "linebreak-style": [ 80 | "error", 81 | "unix" 82 | ], 83 | "lines-around-comment": "error", 84 | "max-depth": "error", 85 | "max-len": [ "error", 120 ], 86 | "max-nested-callbacks": "error", 87 | "max-params": "error", 88 | "new-cap": "error", 89 | "new-parens": "error", 90 | "newline-per-chained-call": "error", 91 | "no-alert": "error", 92 | "no-array-constructor": "error", 93 | "no-bitwise": "error", 94 | "no-caller": "error", 95 | "no-catch-shadow": "error", 96 | "no-confusing-arrow": "error", 97 | "no-continue": "error", 98 | "no-div-regex": "error", 99 | "no-duplicate-imports": "error", 100 | "no-else-return": "error", 101 | "no-empty-function": "error", 102 | "no-eq-null": "error", 103 | "no-eval": "error", 104 | "no-extend-native": "error", 105 | "no-extra-bind": "error", 106 | "no-extra-label": "error", 107 | "no-extra-parens": "error", 108 | "no-floating-decimal": "error", 109 | "no-implicit-coercion": "error", 110 | "no-implicit-globals": "warn", 111 | "no-implied-eval": "error", 112 | "no-inner-declarations": [ 113 | "error", 114 | "functions" 115 | ], 116 | "no-invalid-this": "error", 117 | "no-iterator": "error", 118 | "no-label-var": "error", 119 | "no-labels": "error", 120 | "no-lone-blocks": "error", 121 | "no-lonely-if": "error", 122 | "no-loop-func": "error", 123 | "no-mixed-requires": "error", 124 | "no-multi-spaces": "error", 125 | "no-multi-str": "error", 126 | "no-multiple-empty-lines": "error", 127 | "no-negated-condition": "error", 128 | "no-nested-ternary": "error", 129 | "no-new": "error", 130 | "no-new-func": "error", 131 | "no-new-object": "error", 132 | "no-new-require": "error", 133 | "no-new-wrappers": "error", 134 | "no-octal-escape": "error", 135 | "no-param-reassign": "error", 136 | "no-path-concat": "error", 137 | "no-process-env": "error", 138 | "no-process-exit": "error", 139 | "no-proto": "error", 140 | "no-restricted-globals": "error", 141 | "no-restricted-imports": "error", 142 | "no-restricted-modules": "error", 143 | "no-restricted-syntax": "error", 144 | "no-return-assign": "error", 145 | "no-script-url": "error", 146 | "no-self-compare": "error", 147 | "no-sequences": "error", 148 | "no-shadow": "error", 149 | "no-shadow-restricted-names": "error", 150 | "no-spaced-func": "error", 151 | "no-sync": "error", 152 | "no-ternary": "error", 153 | "no-throw-literal": "error", 154 | "no-trailing-spaces": "error", 155 | "no-undef-init": "error", 156 | "no-undefined": "error", 157 | "no-underscore-dangle": "error", 158 | "no-unmodified-loop-condition": "error", 159 | "no-unneeded-ternary": "error", 160 | "no-use-before-define": "error", 161 | "no-useless-call": "error", 162 | "no-useless-concat": "error", 163 | "no-useless-constructor": "error", 164 | "no-useless-escape": "error", 165 | "no-void": "error", 166 | "no-whitespace-before-property": "error", 167 | "no-with": "error", 168 | "object-curly-spacing": "error", 169 | "one-var-declaration-per-line": "error", 170 | "operator-assignment": "error", 171 | "operator-linebreak": "error", 172 | "prefer-const": "error", 173 | "prefer-reflect": "error", 174 | "prefer-spread": "error", 175 | "quotes": [ 176 | "error", 177 | "single" 178 | ], 179 | "radix": "error", 180 | "semi": "error", 181 | "semi-spacing": [ 182 | "error", 183 | { 184 | "after": true, 185 | "before": false 186 | } 187 | ], 188 | "sort-imports": "error", 189 | "sort-vars": "error", 190 | "space-before-blocks": "off", 191 | "space-before-function-paren": "off", 192 | "space-in-parens": "off", 193 | "space-infix-ops": "error", 194 | "space-unary-ops": "error", 195 | "spaced-comment": [ 196 | "error", 197 | "always" 198 | ], 199 | "strict": [ 200 | "error", 201 | "never" 202 | ], 203 | "template-curly-spacing": "error", 204 | "valid-jsdoc": "error", 205 | "wrap-iife": "error", 206 | "wrap-regex": "error", 207 | "yield-star-spacing": "error", 208 | "yoda": [ 209 | "error", 210 | "never" 211 | ] 212 | } 213 | }; 214 | -------------------------------------------------------------------------------- /tests/app/arrays.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/arrays'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('arrays', function() { 7 | var a; 8 | 9 | beforeEach(function() { 10 | a = [ 1, 2, 3, 4 ]; 11 | }); 12 | 13 | it('you should be able to determine the location of an item in an array', function() { 14 | expect(arraysAnswers.indexOf(a, 3)).to.eql(2); 15 | expect(arraysAnswers.indexOf(a, 5)).to.eql(-1); 16 | }); 17 | 18 | it('you should be able to sum the items of an array', function() { 19 | expect(arraysAnswers.sum(a)).to.eql(10); 20 | }); 21 | 22 | it('you should be able to remove all instances of a value from an array', function() { 23 | a.push(2); // Make sure the value appears more than one time 24 | a.push(2); // Make sure the value appears more than one time in a row 25 | var result = arraysAnswers.remove(a, 2); 26 | 27 | expect(result).to.have.length(3); 28 | expect(result.join(' ')).to.eql('1 3 4'); 29 | }); 30 | 31 | it('you should be able to remove all instances of a value from an array, returning the original array', function() { 32 | a.splice( 1, 0, 2 ); 33 | a.push( 2 ); 34 | a.push( 2 ); 35 | 36 | var result = arraysAnswers.removeWithoutCopy(a, 2); 37 | 38 | expect(result).to.have.length(3); 39 | expect(result.join(' ')).to.eql('1 3 4'); 40 | 41 | // make sure that you return the same array instance 42 | expect(result).equal(a); 43 | }); 44 | 45 | it('you should be able to add an item to the end of an array', function() { 46 | var result = arraysAnswers.append(a, 10); 47 | 48 | expect(result).to.have.length(5); 49 | expect(result[result.length - 1]).to.eql(10); 50 | }); 51 | 52 | it('you should be able to remove the last item of an array', function() { 53 | var result = arraysAnswers.truncate(a); 54 | 55 | expect(result).to.have.length(3); 56 | expect(result.join(' ')).to.eql('1 2 3'); 57 | }); 58 | 59 | it('you should be able to add an item to the beginning of an array', function () { 60 | var result = arraysAnswers.prepend(a, 10); 61 | 62 | expect(result).to.have.length(5); 63 | expect(result[0]).to.eql(10); 64 | }); 65 | 66 | it('you should be able to remove the first item of an array', function () { 67 | var result = arraysAnswers.curtail(a); 68 | 69 | expect(result).to.have.length(3); 70 | expect(result.join(' ')).to.eql('2 3 4'); 71 | }); 72 | 73 | it('you should be able to join together two arrays', function() { 74 | var c = [ 'a', 'b', 'c', 1 ]; 75 | var result = arraysAnswers.concat(a, c); 76 | 77 | expect(result).to.have.length(8); 78 | expect(result.join(' ')).to.eql('1 2 3 4 a b c 1'); 79 | }); 80 | 81 | it('you should be able to add an item anywhere in an array', function() { 82 | var result = arraysAnswers.insert(a, 'z', 2); 83 | 84 | expect(result).to.have.length(5); 85 | expect(result.join(' ')).to.eql('1 2 z 3 4'); 86 | }); 87 | 88 | it('you should be able to count the occurences of an item in an array', function() { 89 | var result = arraysAnswers.count([ 1, 2, 4, 4, 3, 4, 3 ], 4); 90 | 91 | expect(result).to.eql(3); 92 | }); 93 | 94 | it('you should be able to find duplicates in an array', function() { 95 | var result = arraysAnswers.duplicates([ 1, 2, 4, 4, 3, 3, 1, 5, 3 ]); 96 | 97 | expect(result.sort()).to.eql([1, 3, 4]); 98 | }); 99 | 100 | it('you should be able to square each number in an array', function() { 101 | var result = arraysAnswers.square(a); 102 | 103 | expect(result).to.have.length(4); 104 | expect(result.join(' ')).to.eql('1 4 9 16'); 105 | }); 106 | 107 | it('you should be able to find all occurrences of an item in an array', function() { 108 | var result = arraysAnswers.findAllOccurrences([ 1, 2, 3, 4, 5, 6, 1, 7], 1); 109 | 110 | expect(result.sort().join(' ')).to.eql('0 6'); 111 | }); 112 | 113 | }); 114 | -------------------------------------------------------------------------------- /tests/app/async.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/async'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('async behavior', function() { 7 | it('you should understand how to use promises to handle asynchronicity', function(done) { 8 | var flag = false; 9 | var finished = 0; 10 | var total = 2; 11 | 12 | function finish(_done) { 13 | if (++finished === total) { _done(); } 14 | } 15 | 16 | asyncAnswers.async(true).then(function(result) { 17 | flag = result; 18 | expect(flag).to.eql(true); 19 | finish(done); 20 | }); 21 | 22 | asyncAnswers.async('success').then(function(result) { 23 | flag = result; 24 | expect(flag).to.eql('success'); 25 | finish(done); 26 | }); 27 | 28 | expect(flag).to.eql(false); 29 | }); 30 | 31 | it('you should be able to retrieve data from the server and return a sorted array of names', function(done) { 32 | var url = '/data/testdata.json'; 33 | 34 | asyncAnswers.manipulateRemoteData(url).then(function(result) { 35 | expect(result).to.have.length(5); 36 | expect(result.join(' ')).to.eql('Adam Alex Matt Paul Rebecca'); 37 | done(); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /tests/app/bestPractices.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/bestPractices'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('best practices', function(){ 7 | it('you should avoid global variables', function() { 8 | bestPracticesAnswers.globals(); 9 | expect(window.myObject).not.to.be.ok; 10 | }); 11 | 12 | it('you should use parseInt correctly', function() { 13 | expect(bestPracticesAnswers.parseInt('12')).to.eql(12); 14 | expect(bestPracticesAnswers.parseInt('12px')).to.eql(12); 15 | expect(bestPracticesAnswers.parseInt('0x12')).to.eql(0); 16 | }); 17 | 18 | it('you should understand strict comparison', function() { 19 | expect(bestPracticesAnswers.identity(1, '1')).to.eql(false); 20 | expect(bestPracticesAnswers.identity(1, 1)).to.eql(true); 21 | expect(bestPracticesAnswers.identity(0, false)).to.eql(false); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/app/count.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | if ( typeof window === 'undefined' ) { 3 | require('../../app/count'); 4 | var expect = require('chai').expect; 5 | var sinon = require('sinon'); 6 | } 7 | 8 | /** 9 | * This test describes a function, count, that takes two arguments: a starting number, 10 | * and an ending number. The function should console.log each number from the start 11 | * number to the end number, one number per 1/10th of a second. The function should 12 | * return an object with a cancel method, which should cancel the counting. 13 | */ 14 | 15 | describe('counter', function () { 16 | var nums; 17 | var origConsoleLog; 18 | 19 | beforeEach(function () { 20 | nums = []; 21 | 22 | if (typeof console === 'undefined') { 23 | console = { 24 | log: null 25 | }; 26 | } 27 | origConsoleLog = console.log; 28 | console.log = function (val) { 29 | nums.push(val); 30 | }; 31 | 32 | this.clock = sinon.useFakeTimers(); 33 | }); 34 | 35 | afterEach(function () { 36 | console.log = origConsoleLog; 37 | 38 | this.clock.restore(); 39 | }); 40 | 41 | it('should count from start number to end number, one per 1/10th of a second', function () { 42 | this.timeout(600); 43 | countAnswers.count(1, 5); 44 | 45 | for (var i = 1; i <= 5; i++) { 46 | expect(nums.length).to.eql(i); 47 | 48 | this.clock.tick(100); 49 | } 50 | 51 | expect(nums.length).to.eql(5); 52 | expect(nums[0]).to.eql(1); 53 | expect(nums[4]).to.eql(5); 54 | }); 55 | 56 | it('should provide a method to cancel the counting', function () { 57 | this.timeout(600); 58 | 59 | var counter = countAnswers.count(1, 5); 60 | counter.cancel(); 61 | 62 | this.clock.tick(550); 63 | 64 | expect(nums.length < 5).to.be.ok; 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /tests/app/flowControl.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/flowControl'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('flow control', function() { 7 | it('you should be able to conditionally branch your code', function() { 8 | var num = 0; 9 | 10 | while (num % 3 === 0 || num % 5 === 0) { 11 | num = Math.floor(Math.random() * 10) + 1; 12 | } 13 | 14 | expect(flowControlAnswers.fizzBuzz()).not.to.be.ok; 15 | expect(flowControlAnswers.fizzBuzz('foo')).not.to.be.ok; 16 | expect(flowControlAnswers.fizzBuzz(2)).to.eql(2); 17 | expect(flowControlAnswers.fizzBuzz(101)).to.eql(101); 18 | 19 | expect(flowControlAnswers.fizzBuzz(3)).to.eql('fizz'); 20 | expect(flowControlAnswers.fizzBuzz(6)).to.eql('fizz'); 21 | expect(flowControlAnswers.fizzBuzz(num * 3)).to.eql('fizz'); 22 | 23 | expect(flowControlAnswers.fizzBuzz(5)).to.eql('buzz'); 24 | expect(flowControlAnswers.fizzBuzz(10)).to.eql('buzz'); 25 | expect(flowControlAnswers.fizzBuzz(num * 5)).to.eql('buzz'); 26 | 27 | expect(flowControlAnswers.fizzBuzz(15)).to.eql('fizzbuzz'); 28 | expect(flowControlAnswers.fizzBuzz(num * 3 * 5)).to.eql('fizzbuzz'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /tests/app/functions.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/functions'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('functions', function() { 7 | var sayItCalled = false; 8 | var sayIt = function(greeting, name, punctuation) { 9 | sayItCalled = true; 10 | return greeting + ', ' + name + (punctuation || '!'); 11 | }; 12 | 13 | beforeEach(function () { 14 | sayItCalled = false; 15 | }); 16 | 17 | it('you should be able to use an array as arguments when calling a function', function() { 18 | var result = functionsAnswers.argsAsArray(sayIt, [ 'Hello', 'Ellie', '!' ]); 19 | expect(result).to.eql('Hello, Ellie!'); 20 | expect(sayItCalled).to.be.ok; 21 | }); 22 | 23 | it('you should be able to change the context in which a function is called', function() { 24 | var speak = function() { 25 | return sayIt(this.greeting, this.name, '!!!'); 26 | }; 27 | var obj = { 28 | greeting: 'Hello', 29 | name: 'Rebecca' 30 | }; 31 | 32 | var result = functionsAnswers.speak(speak, obj); 33 | expect(result).to.eql('Hello, Rebecca!!!'); 34 | expect(sayItCalled).to.be.ok; 35 | }); 36 | 37 | it('you should be able to return a function from a function', function() { 38 | expect(functionsAnswers.functionFunction('Hello')('world')).to.eql('Hello, world'); 39 | expect(functionsAnswers.functionFunction('Hai')('can i haz funxtion?')).to.eql('Hai, can i haz funxtion?'); 40 | }); 41 | 42 | it('you should be able to use closures', function () { 43 | var arr = [ Math.random(), Math.random(), Math.random(), Math.random() ]; 44 | var square = function (x) { return x * x; }; 45 | 46 | var funcs = functionsAnswers.makeClosures(arr, square); 47 | expect(funcs).to.have.length(arr.length); 48 | 49 | for (var i = 0; i < arr.length; i++) { 50 | expect(funcs[i]()).to.eql(square(arr[i])); 51 | } 52 | }); 53 | 54 | it('you should be able to create a "partial" function', function() { 55 | var partial = functionsAnswers.partial(sayIt, 'Hello', 'Ellie'); 56 | expect(partial('!!!')).to.eql('Hello, Ellie!!!'); 57 | expect(sayItCalled).to.be.ok; 58 | }); 59 | 60 | it('you should be able to use arguments', function () { 61 | var a = Math.random(); 62 | var b = Math.random(); 63 | var c = Math.random(); 64 | var d = Math.random(); 65 | 66 | expect(functionsAnswers.useArguments(a)).to.eql(a); 67 | expect(functionsAnswers.useArguments(a, b)).to.eql(a + b); 68 | expect(functionsAnswers.useArguments(a, b, c)).to.eql(a + b + c); 69 | expect(functionsAnswers.useArguments(a, b, c, d)).to.eql(a + b + c + d); 70 | }); 71 | 72 | it('you should be able to apply functions with arbitrary numbers of arguments', function () { 73 | (function () { 74 | var a = Math.random(); 75 | var b = Math.random(); 76 | var c = Math.random(); 77 | 78 | var wasITake2ArgumentsCalled = false; 79 | var iTake2Arguments = function (firstArgument, secondArgument) { 80 | expect(arguments.length).to.eql(2); 81 | expect(firstArgument).to.eql(a); 82 | expect(secondArgument).to.eql(b); 83 | 84 | wasITake2ArgumentsCalled = true; 85 | }; 86 | 87 | var wasITake3ArgumentsCalled = false; 88 | var iTake3Arguments = function (firstArgument, secondArgument, thirdArgument) { 89 | expect(arguments.length).to.eql(3); 90 | expect(firstArgument).to.eql(a); 91 | expect(secondArgument).to.eql(b); 92 | expect(thirdArgument).to.eql(c); 93 | 94 | wasITake3ArgumentsCalled = true; 95 | }; 96 | 97 | functionsAnswers.callIt(iTake2Arguments, a, b); 98 | functionsAnswers.callIt(iTake3Arguments, a, b, c); 99 | 100 | expect(wasITake2ArgumentsCalled).to.be.ok; 101 | expect(wasITake3ArgumentsCalled).to.be.ok; 102 | }()); 103 | }); 104 | 105 | it('you should be able to create a "partial" function for variable number of applied arguments', function () { 106 | var partialMe = function (x, y, z) { 107 | return x / y * z; 108 | }; 109 | 110 | var a = Math.random(); 111 | var b = Math.random(); 112 | var c = Math.random(); 113 | expect(functionsAnswers.partialUsingArguments(partialMe)(a, b, c)).to.eql(partialMe(a, b, c)); 114 | expect(functionsAnswers.partialUsingArguments(partialMe, a)(b, c)).to.eql(partialMe(a, b, c)); 115 | expect(functionsAnswers.partialUsingArguments(partialMe, a, b)(c)).to.eql(partialMe(a, b, c)); 116 | expect(functionsAnswers.partialUsingArguments(partialMe, a, b, c)()).to.eql(partialMe(a, b, c)); 117 | }); 118 | 119 | it('you should be able to curry existing functions', function () { 120 | var curryMe = function (x, y, z) { 121 | return x / y * z; 122 | }; 123 | 124 | var a = Math.random(); 125 | var b = Math.random(); 126 | var c = Math.random(); 127 | var result; 128 | 129 | result = functionsAnswers.curryIt(curryMe); 130 | expect(typeof result).to.eql('function'); 131 | expect(result.length).to.eql(1); 132 | 133 | result = functionsAnswers.curryIt(curryMe)(a); 134 | expect(typeof result).to.eql('function'); 135 | expect(result.length).to.eql(1); 136 | 137 | result = functionsAnswers.curryIt(curryMe)(a)(b); 138 | expect(typeof result).to.eql('function'); 139 | expect(result.length).to.eql(1); 140 | 141 | result = functionsAnswers.curryIt(curryMe)(a)(b)(c); 142 | expect(typeof result).to.eql('number'); 143 | expect(result).to.eql(curryMe(a, b, c)); 144 | }); 145 | }); 146 | -------------------------------------------------------------------------------- /tests/app/logicalOperators.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/logicalOperators'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('logical operators', function(){ 7 | it('you should be able to work with logical or', function() { 8 | expect(logicalOperatorsAnswers.or(false, true)).to.be.ok; 9 | expect(logicalOperatorsAnswers.or(true, false)).to.be.ok; 10 | expect(logicalOperatorsAnswers.or(true, true)).to.be.ok; 11 | expect(logicalOperatorsAnswers.or(false, false)).not.to.be.ok; 12 | expect(logicalOperatorsAnswers.or(3, 4)).to.not.eq(7); 13 | }); 14 | 15 | it('you should be able to work with logical and', function() { 16 | expect(logicalOperatorsAnswers.and(false, true)).not.to.be.ok; 17 | expect(logicalOperatorsAnswers.and(false, false)).not.to.be.ok; 18 | expect(logicalOperatorsAnswers.and(true, false)).not.to.be.ok; 19 | expect(logicalOperatorsAnswers.and(true, true)).to.be.ok; 20 | expect(logicalOperatorsAnswers.and(3, 4)).to.be.ok; 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/app/modules.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/modules'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('the module pattern', function() { 7 | it('you should be able to create a function that returns a module', function() { 8 | var module = modulesAnswers.createModule('hello', 'matt'); 9 | 10 | expect(module.sayIt).to.be.a('function'); 11 | expect(module.name).to.eql('matt'); 12 | expect(module.greeting).to.eql('hello'); 13 | expect(module.sayIt()).to.eql('hello, matt'); 14 | 15 | module.name = 'katniss'; 16 | module.greeting = 'hi'; 17 | expect(module.name).to.eql('katniss'); 18 | expect(module.greeting).to.eql('hi'); 19 | expect(module.sayIt()).to.eql('hi, katniss'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/app/numbers.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/numbers'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('numbers', function() { 7 | describe('binary operations', function() { 8 | it('you should be able to find the value of a given bit', function() { 9 | expect(numbersAnswers.valueAtBit(128, 8)).to.eql(1); 10 | expect(numbersAnswers.valueAtBit(65, 1)).to.eql(1); 11 | expect(numbersAnswers.valueAtBit(65, 7)).to.eql(1); 12 | expect(numbersAnswers.valueAtBit(128, 1)).to.eql(0); 13 | }); 14 | 15 | it('you should be able to return the base10 representation of a binary string', function() { 16 | expect(numbersAnswers.base10('11000000')).to.eql(192); 17 | }); 18 | 19 | it('you should be able to convert an eight-bit number to a binary string', function() { 20 | expect(numbersAnswers.convertToBinary(128)).to.eql('10000000'); 21 | expect(numbersAnswers.convertToBinary(65)).to.eql('01000001'); 22 | }); 23 | }); 24 | 25 | describe('decimals', function() { 26 | it('you should be able to multiply with precision', function() { 27 | expect(numbersAnswers.multiply(3, 0.1)).to.eql(0.3); 28 | expect(numbersAnswers.multiply(3, 0.2)).to.eql(0.6); 29 | expect(numbersAnswers.multiply(3, 0.0001)).to.eql(0.0003); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/app/objects.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/objects'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('objects and context', function() { 7 | var a; 8 | var b; 9 | var C; 10 | 11 | beforeEach(function() { 12 | a = { 13 | name: 'Matt', 14 | greeting: 'Hello', 15 | sayIt: function() { 16 | return this.greeting + ', ' + 17 | this.name + '!'; 18 | } 19 | }; 20 | 21 | b = { 22 | name: 'Rebecca', 23 | greeting: 'Yo' 24 | }; 25 | 26 | C = function(name) { 27 | this.name = name; 28 | return this; 29 | }; 30 | }); 31 | 32 | it('you should be able to alter the context in which a method runs', function() { 33 | // define a function for fn so that the following will pass 34 | expect(objectsAnswers.alterContext(a.sayIt, b)).to.eql('Yo, Rebecca!'); 35 | }); 36 | 37 | it('you should be able to alter multiple objects at once', function() { 38 | // define a function for fn so that the following will pass 39 | var obj1 = new C('Rebecca'); 40 | var obj2 = new C('Melissa'); 41 | var greeting = 'What\'s up'; 42 | 43 | objectsAnswers.alterObjects(C, greeting); 44 | 45 | expect(obj1.greeting).to.eql(greeting); 46 | expect(obj2.greeting).to.eql(greeting); 47 | expect(new C('Ellie').greeting).to.eql(greeting); 48 | }); 49 | 50 | it('you should be able to iterate over an object\'s "own" properties', function() { 51 | // define a function for fn so that the following will pass 52 | C = function() { 53 | this.foo = 'bar'; 54 | this.baz = 'bim'; 55 | }; 56 | 57 | C.prototype.bop = 'bip'; 58 | 59 | var obj = new C(); 60 | 61 | expect(objectsAnswers.iterate(obj)).to.eql([ 'foo: bar', 'baz: bim' ]); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /tests/app/recursion.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/recursion'); 3 | var expect = require('chai').expect; 4 | var _ = require('underscore'); 5 | } 6 | 7 | describe('recursion', function() { 8 | var fileData = { 9 | dir: 'app', 10 | files: [ 11 | 'index.html', 12 | { 13 | dir: 'js', 14 | files: [ 15 | 'main.js', 16 | 'app.js', 17 | 'misc.js', 18 | { 19 | dir: 'vendor', 20 | files: [ 21 | 'jquery.js', 22 | 'underscore.js' 23 | ] 24 | } 25 | ] 26 | }, 27 | { 28 | dir: 'css', 29 | files: [ 30 | 'reset.css', 31 | 'main.css' 32 | ] 33 | } 34 | ] 35 | }; 36 | 37 | it('you should be able to return a list of files from the data', function() { 38 | var result = recursionAnswers.listFiles(fileData); 39 | expect(result.length).to.eql(8); 40 | expect(result.indexOf('index.html') > -1).to.be.ok; 41 | expect(result.indexOf('main.js') > -1).to.be.ok; 42 | expect(result.indexOf('underscore.js') > -1).to.be.ok; 43 | }); 44 | 45 | it('you should be able to return a list of files in a subdir', function() { 46 | var result = recursionAnswers.listFiles(fileData, 'js'); 47 | expect(result.length).to.eql(5); 48 | expect(result.indexOf('main.js') > -1).to.be.ok; 49 | expect(result.indexOf('underscore.js') > -1).to.be.ok; 50 | }); 51 | }); 52 | 53 | describe('permutation', function() { 54 | var arr = [ 1, 2, 3, 4 ]; 55 | var answer = [ 56 | [1, 2, 3, 4], 57 | [1, 2, 4, 3], 58 | [1, 3, 2, 4], 59 | [1, 3, 4, 2], 60 | [1, 4, 2, 3], 61 | [1, 4, 3, 2], 62 | [2, 1, 3, 4], 63 | [2, 1, 4, 3], 64 | [2, 3, 1, 4], 65 | [2, 3, 4, 1], 66 | [2, 4, 1, 3], 67 | [2, 4, 3, 1], 68 | [3, 1, 2, 4], 69 | [3, 1, 4, 2], 70 | [3, 2, 1, 4], 71 | [3, 2, 4, 1], 72 | [3, 4, 1, 2], 73 | [3, 4, 2, 1], 74 | [4, 1, 2, 3], 75 | [4, 1, 3, 2], 76 | [4, 2, 1, 3], 77 | [4, 2, 3, 1], 78 | [4, 3, 1, 2], 79 | [4, 3, 2, 1] 80 | ]; 81 | 82 | it('you should be able to return the permutations of an array', function() { 83 | var result = recursionAnswers.permute(arr); 84 | var resultStrings = _.map(result, function(r) { return r.join(''); }); 85 | 86 | expect(result.length).to.eql(answer.length); 87 | 88 | _.each(answer, function(a) { 89 | expect(resultStrings.indexOf(a.join('')) > -1).to.be.ok; 90 | }); 91 | }); 92 | 93 | it('you should be able to return the nth number in a fibonacci sequence', function() { 94 | expect(recursionAnswers.fibonacci(2)).to.eql(1); 95 | expect(recursionAnswers.fibonacci(6)).to.eql(8); 96 | }); 97 | 98 | it('you should be able to return the set of all valid combinations of n pairs of parentheses.', function() { 99 | var expected = [ '((()))', '(()())', '(())()', '()(())', '()()()']; 100 | var result = recursionAnswers.validParentheses(3); 101 | 102 | expect(result.length).to.eql(5); 103 | _.each(expected, function(c) { 104 | expect(result).to.contain(c); 105 | }); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /tests/app/regex.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/regex'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('regular expressions', function() { 7 | it('you should be able to detect a number in a string', function() { 8 | expect(regexAnswers.containsNumber('abc123')).to.eql(true); 9 | expect(regexAnswers.containsNumber('abc')).to.eql(false); 10 | }); 11 | 12 | it('you should be able to detect a repeating letter in a string', function() { 13 | expect(regexAnswers.containsRepeatingLetter('bookkeeping')).to.eql(true); 14 | expect(regexAnswers.containsRepeatingLetter('rattler')).to.eql(true); 15 | expect(regexAnswers.containsRepeatingLetter('ZEPPELIN')).to.eql(true); 16 | expect(regexAnswers.containsRepeatingLetter('cats')).to.eql(false); 17 | expect(regexAnswers.containsRepeatingLetter('l33t')).to.eql(false); 18 | }); 19 | 20 | it('you should be able to determine whether a string ends with a vowel (aeiou)', function() { 21 | expect(regexAnswers.endsWithVowel('cats')).to.eql(false); 22 | expect(regexAnswers.endsWithVowel('gorilla')).to.eql(true); 23 | expect(regexAnswers.endsWithVowel('I KNOW KUNG FU')).to.eql(true); 24 | }); 25 | 26 | it('you should be able to capture the first series of three numbers', function() { 27 | expect(regexAnswers.captureThreeNumbers('abc123')).to.eql('123'); 28 | expect(regexAnswers.captureThreeNumbers('9876543')).to.eql('987'); 29 | expect(regexAnswers.captureThreeNumbers('abcdef')).to.eql(false); 30 | expect(regexAnswers.captureThreeNumbers('12ab12ab')).to.eql(false); 31 | }); 32 | 33 | it('you should be able to determine whether a string matches a pattern', function() { 34 | // the pattern is XXX-XXX-XXXX where all X's are digits 35 | expect(regexAnswers.matchesPattern('800-555-1212')).to.eql(true); 36 | expect(regexAnswers.matchesPattern('451-933-7899')).to.eql(true); 37 | expect(regexAnswers.matchesPattern('33-444-5555')).to.eql(false); 38 | expect(regexAnswers.matchesPattern('abc-def-hijk')).to.eql(false); 39 | expect(regexAnswers.matchesPattern('1800-555-1212')).to.eql(false); 40 | expect(regexAnswers.matchesPattern('800-555-12121')).to.eql(false); 41 | expect(regexAnswers.matchesPattern('800-5555-1212')).to.eql(false); 42 | expect(regexAnswers.matchesPattern('800-55-1212')).to.eql(false); 43 | }); 44 | 45 | it('you should be able to detect correctly-formatted monetary amounts in USD', function() { 46 | expect(regexAnswers.isUSD('$132.03')).to.eql(true); 47 | expect(regexAnswers.isUSD('$32.03')).to.eql(true); 48 | expect(regexAnswers.isUSD('$2.03')).to.eql(true); 49 | expect(regexAnswers.isUSD('$1,023,032.03')).to.eql(true); 50 | expect(regexAnswers.isUSD('$20,933,209.93')).to.eql(true); 51 | expect(regexAnswers.isUSD('$20,933,209')).to.eql(true); 52 | expect(regexAnswers.isUSD('$459,049,393.21')).to.eql(true); 53 | expect(regexAnswers.isUSD('34,344.34')).to.eql(false); 54 | expect(regexAnswers.isUSD('$,344.34')).to.eql(false); 55 | expect(regexAnswers.isUSD('$34,344.3')).to.eql(false); 56 | expect(regexAnswers.isUSD('$34,344.344')).to.eql(false); 57 | expect(regexAnswers.isUSD('$34,344_34')).to.eql(false); 58 | expect(regexAnswers.isUSD('$3,432,12.12')).to.eql(false); 59 | expect(regexAnswers.isUSD('$3,432,1,034.12')).to.eql(false); 60 | expect(regexAnswers.isUSD('4$3,432,034.12')).to.eql(false); 61 | expect(regexAnswers.isUSD('$2.03.45')).to.eql(false); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /tests/app/strings.js: -------------------------------------------------------------------------------- 1 | if ( typeof window === 'undefined' ) { 2 | require('../../app/strings'); 3 | var expect = require('chai').expect; 4 | } 5 | 6 | describe('strings', function() { 7 | it('you should be able to reduce duplicate characters to a desired minimum', function() { 8 | expect(stringsAnswers.reduceString('aaaabbbb', 2)).to.eql('aabb'); 9 | expect(stringsAnswers.reduceString('xaaabbbb', 2)).to.eql('xaabb'); 10 | expect(stringsAnswers.reduceString('aaaabbbb', 1)).to.eql('ab'); 11 | expect(stringsAnswers.reduceString('aaxxxaabbbb', 2)).to.eql('aaxxaabb'); 12 | }); 13 | 14 | it('you should be able to wrap lines at a given number of columns, without breaking words', function() { 15 | var wrapCol = 5; 16 | var inputStrings = [ 17 | 'abcdef abcde abc def', 18 | 'abc abc abc', 19 | 'a b c def' 20 | ]; 21 | var outputStrings = [ 22 | 'abcdef\nabcde\nabc\ndef', 23 | 'abc\nabc\nabc', 24 | 'a b c\ndef' 25 | ]; 26 | var formattedStr; 27 | 28 | inputStrings.forEach(function(str, index) { 29 | formattedStr = stringsAnswers.wordWrap(str, wrapCol); 30 | expect(formattedStr).to.eql(outputStrings[index]); 31 | }); 32 | }); 33 | 34 | it('you should be able to reverse a string', function() { 35 | var inputStrings = [ 36 | 'abc', 37 | 'i am a string of characters', 38 | 'A man, a plan, a canal: Panama' 39 | ]; 40 | var outputStrings = [ 41 | 'cba', 42 | 'sretcarahc fo gnirts a ma i', 43 | 'amanaP :lanac a ,nalp a ,nam A' 44 | ]; 45 | 46 | inputStrings.forEach(function(str, index) { 47 | var result = stringsAnswers.reverseString(str); 48 | expect(result).to.eql(outputStrings[index]); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /tests/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mocha Tests 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | --------------------------------------------------------------------------------