From 8157c6e20cac99e3cbcc2e513a18f7073a21b07f Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Sat, 24 Oct 2015 12:45:30 +0530 Subject: [PATCH 01/33] Temporary fix to bugs due to removeComments method-closes #3763 --- client/commonFramework.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/commonFramework.js b/client/commonFramework.js index c100edd30d..a8d8e8486e 100644 --- a/client/commonFramework.js +++ b/client/commonFramework.js @@ -767,7 +767,7 @@ var scrapeTests = function(userJavaScript) { }; function removeComments(userJavaScript) { - var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|\/\/[^\n]*/g); + var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|([ \n]\/\/[^\n]*)/g); return userJavaScript.replace(regex, ''); } From 7fd5174fe0bd97539a9166f527b1256dee6b7530 Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Thu, 29 Oct 2015 00:13:20 -0700 Subject: [PATCH 02/33] Fix Ctrl-Enter doesn't work when not logged in. Closes #3900 First time contributing real code, so please let me know any critiques or style changes I should make. --- client/commonFramework.js | 6 +++++- server/views/coursewares/showBonfire.jade | 2 +- server/views/coursewares/showHTML.jade | 2 +- server/views/coursewares/showJS.jade | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/client/commonFramework.js b/client/commonFramework.js index 2505fe08f4..559592bfbe 100644 --- a/client/commonFramework.js +++ b/client/commonFramework.js @@ -659,7 +659,11 @@ function ctrlEnterClickHandler(e) { // ctrl + enter if (e.ctrlKey && e.keyCode === 13) { $('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler); - $('#submit-challenge').click(); + if ($('#submit-challenge').length > 0) { + $('#submit-challenge').click(); + } else { + window.location = '/challenges/next-challenge?id=' + common.challengeId; + } } } diff --git a/server/views/coursewares/showBonfire.jade b/server/views/coursewares/showBonfire.jade index 92432a3bf4..5a2f6c0a11 100644 --- a/server/views/coursewares/showBonfire.jade +++ b/server/views/coursewares/showBonfire.jade @@ -108,7 +108,7 @@ block content i.fa.fa-twitter   = phrase else - a.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) Go to my next challenge + #next-challenge.btn.btn-lg.btn-primary.btn-block Go to my next challenge (ctrl + enter) #reset-modal.modal(tabindex='-1') .modal-dialog.animated.fadeInUp.fast-animation .modal-content diff --git a/server/views/coursewares/showHTML.jade b/server/views/coursewares/showHTML.jade index ad2213e803..d07dd7748d 100644 --- a/server/views/coursewares/showHTML.jade +++ b/server/views/coursewares/showHTML.jade @@ -98,7 +98,7 @@ block content if(user) #submit-challenge.animated.fadeIn.btn.btn-lg.btn-primary.btn-block Submit and go to my next challenge (ctrl + enter) else - a.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) Go to my next challenge + #next-challenge.btn.btn-lg.btn-primary.btn-block Go to my next challenge (ctrl + enter) include ../partials/challenge-modals script. document.addEventListener('gitter-sidecar-ready', function(e) { diff --git a/server/views/coursewares/showJS.jade b/server/views/coursewares/showJS.jade index 887e44c964..4cfe47bf96 100644 --- a/server/views/coursewares/showJS.jade +++ b/server/views/coursewares/showJS.jade @@ -95,7 +95,7 @@ block content if (user) #submit-challenge.animated.fadeIn.btn.btn-lg.btn-primary.btn-block Submit and go to my next challenge (ctrl + enter) else - a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) Go to my next challenge + #next-challenge.animated.fadeIn.btn.btn-lg.btn-primary.btn-block Go to my next challenge (ctrl + enter) include ../partials/challenge-modals script. var MDNlinks = !{JSON.stringify(MDNlinks)}; From 5c714efa4e6f3b2ae45977660cd38c5811d77911 Mon Sep 17 00:00:00 2001 From: Abhisek Pattnaik Date: Thu, 29 Oct 2015 17:19:29 +0530 Subject: [PATCH 03/33] for loop clarification - fix typo - variable - Instruction clarification - Example clarification --- seed/challenges/basic-javascript.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index b22ee617c7..6f7fa03251 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -868,16 +868,16 @@ "The most common type of JavaScript loop is called a \"for loop\" because it runs \"for\" a specific number of times.", "For loops are declared with three optional expressions seperated by semicolons:", "for([initialization]; [condition]; [final-expression])", - "The initialization statement is executed one time only before the loop starts. It is typically used to define and setup your loop varaible.", - "The condition statement is evaluated at the beginning of every loop and will continue as long as it evalutes true. When condition is false at the start of the loop, the loop will stop executing. This means if condition starts as false, your loop will never execute.", + "The initialization statement is executed one time only before the loop starts. It is typically used to define and setup your loop variable.", + "The condition statement is evaluated at the beginning of every loop iteration and will continue as long as it evalutes to true. When condition is false at the start of the iteration, the loop will stop executing. This means if condition starts as false, your loop will never execute.", "The final-expression is executed at the end of each loop iteration, prior to the next condition check and is usually used to increment or decrement your loop counter.", - "We'll initialize with i = 0 and loop while our condition i < 5 is true. We'll increment i by 1 each loop with i++ as our final-expression.", + "In the following example we initialize with i = 0 and iterate while our condition i < 5 is true. We'll increment i by 1 in each loop iteration with i++ as our final-expression.", "var ourArray = [];", "for(var i = 0; i < 5; i++) {", "  ourArray.push(i);", "}", - "ourArray will now contain [0,1,2,3,4] ", - "Let's try getting a for loop to work by pushing values to an array." + "ourArray will now contain [0,1,2,3,4].", + "Let's try getting a for loop to work by pushing values to an array." ], "tests": [ "assert(editor.getValue().match(/for\\s*\\(/g).length > 1, 'message: You should be using a for loop for this.');", From c3bcc9fbc804245e49ee5cc1b6ce97427408099f Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Thu, 29 Oct 2015 20:05:48 -0700 Subject: [PATCH 04/33] Fix Nest Anchor in Paragraph Test Closes #4012 and closes #3989 --- seed/challenges/html5-and-css.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/html5-and-css.json b/seed/challenges/html5-and-css.json index 4372a5cf6b..9ae6a4ad8b 100644 --- a/seed/challenges/html5-and-css.json +++ b/seed/challenges/html5-and-css.json @@ -1172,7 +1172,7 @@ "assert($(\"a\").text().match(/cat\\sphotos/gi), 'Your a element should have the anchor text of \"cat photos\"')", "assert($(\"p\") && $(\"p\").length > 2, 'Create a new p element around your a element.')", "assert($(\"a[href=\\\"http://www.freecatphotoapp.com\\\"]\").parent().is(\"p\"), 'Your a element should be nested within your new p element.')", - "assert($(\"p\").text().match(/^\\s*View\\smore\\s/gi), 'Your p element should have the text \"View more \" (with a space after it).')", + "assert($(\"a[href=\\\"http://www.freecatphotoapp.com\\\"]\").parent().text().match(/^\\s*View\\smore\\s/gi), 'Your p element should have the text \"View more \" (with a space after it).')", "assert(!$(\"a\").text().match(/View\\smore/gi), 'Your a element should not have the text \"View more\".')", "assert(editor.match(/<\\/p>/g) && editor.match(/

/g).length === editor.match(/

p elements has a closing tag.')", "assert(editor.match(/<\\/a>/g) && editor.match(//g).length === editor.match(/a elements has a closing tag.')" From 21014c2b11074b715d3855c568b59ae5f8d56886 Mon Sep 17 00:00:00 2001 From: Utsav Ahuja Date: Fri, 30 Oct 2015 02:25:19 -0700 Subject: [PATCH 05/33] Removed Chai.js mention from Bonfire --- seed/challenges/basic-bonfires.json | 1 - 1 file changed, 1 deletion(-) diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json index 8ac6cbcae7..f81241c9d2 100644 --- a/seed/challenges/basic-bonfires.json +++ b/seed/challenges/basic-bonfires.json @@ -273,7 +273,6 @@ "description": [ "Return an array consisting of the largest number from each provided sub-array. For simplicity, the provided array will contain exactly 4 sub-arrays.", "Remember, you can iterate through an array with a simple for loop, and access each member with array syntax arr[i] .", - "If you are writing your own Chai.js tests, be sure to use a deep equal statement instead of an equal statement when comparing arrays.", "Remember to use Read-Search-Ask if you get stuck. Write your own code." ], "challengeSeed": [ From 6ef376a6211974e13cc6a998203e8787ea4e30a6 Mon Sep 17 00:00:00 2001 From: Martin Delille Date: Thu, 29 Oct 2015 11:01:35 +0100 Subject: [PATCH 06/33] fix typo and add comma latitiude => latitude add space and
--- seed/challenges/json-apis-and-ajax.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/json-apis-and-ajax.json b/seed/challenges/json-apis-and-ajax.json index ea3a4351e2..76b89f1688 100644 --- a/seed/challenges/json-apis-and-ajax.json +++ b/seed/challenges/json-apis-and-ajax.json @@ -377,7 +377,7 @@ "Here's some code that does this:", "if (navigator.geolocation) {", "  navigator.geolocation.getCurrentPosition(function(position) {", - "    $(\"#data\").html(\"latitiude\" + position.coords.latitude + \"longitude\" + position.coords.longitude);", + "    $(\"#data\").html(\"latitude: \" + position.coords.latitude + \"<br>longitude: \" + position.coords.longitude);", "  });", "}" ], From fb0e78fc3a8ff16ea6f50926a5da3da0c87ae5f7 Mon Sep 17 00:00:00 2001 From: bencassidy Date: Fri, 30 Oct 2015 09:30:08 -0700 Subject: [PATCH 07/33] fixed typer --- seed/challenges/object-oriented-and-functional-programming.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/object-oriented-and-functional-programming.json b/seed/challenges/object-oriented-and-functional-programming.json index 95c6d73b85..4570dd45ff 100644 --- a/seed/challenges/object-oriented-and-functional-programming.json +++ b/seed/challenges/object-oriented-and-functional-programming.json @@ -330,7 +330,7 @@ "tests": [ "assert.deepEqual(array, [7,6,5,4,3,2,1], 'message: You should reverse the array.');", "assert(editor.getValue().match(/\\.reverse\\s*\\(\\)/gi), 'message: You should use the reverse method.');", - "assert(editor.getValue().match(/\\[1\\,2\\,3\\,4\\,5\\,6\\,7/gi), 'message: You should only be using revserse to modify array.');" + "assert(editor.getValue().match(/\\[1\\,2\\,3\\,4\\,5\\,6\\,7/gi), 'message: You should only be using reverse to modify array.');" ], "challengeSeed": [ "var array = [1,2,3,4,5,6,7];", From c3ec502a91f51a7e3f0a6639509b8fc2e0d58e08 Mon Sep 17 00:00:00 2001 From: pC15 Date: Fri, 30 Oct 2015 11:56:23 -0700 Subject: [PATCH 08/33] fix for issue#4011 changed codepen links to /full/ for Weather challenge --- seed/challenges/intermediate-ziplines.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/intermediate-ziplines.json b/seed/challenges/intermediate-ziplines.json index 0d2afbcb5c..bc8c20c063 100644 --- a/seed/challenges/intermediate-ziplines.json +++ b/seed/challenges/intermediate-ziplines.json @@ -8,7 +8,7 @@ "title": "Show the Local Weather", "challengeSeed": ["126415127"], "description": [ - "Objective: Build a CodePen.io app that successfully reverse-engineers this: http://codepen.io/FreeCodeCamp/pen/avqvgJ.", + "Objective: Build a CodePen.io app that successfully reverse-engineers this: http://codepen.io/FreeCodeCamp/full/avqvgJ.", "Rule #1: Don't look at the example project's code on CodePen. Figure it out for yourself.", "Rule #2: You may use whichever libraries or APIs you need.", "Rule #3: Reverse engineer the example project's functionality, and also feel free to personalize it.", From 086b0bb60cbb6c9c66d24e25b569293c76bcf9bd Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Thu, 29 Oct 2015 21:51:35 -0700 Subject: [PATCH 09/33] JSON/API Lesson Cleanup + Fixes Convert JSON to HTML lesson (Closes #4016) + Add Formatting to Geolocation lesson output (Closes #4010) + Fixes Render Images lesson + Removes unnecessary `?callback=` from JSON challenges. (Closes #3972) + Lots of other formatting and consistancy changes. + Also closes #3962 and closes #3969 --- seed/challenges/json-apis-and-ajax.json | 288 ++++++++++++------------ 1 file changed, 140 insertions(+), 148 deletions(-) diff --git a/seed/challenges/json-apis-and-ajax.json b/seed/challenges/json-apis-and-ajax.json index d0373c1d75..940cb95bdb 100644 --- a/seed/challenges/json-apis-and-ajax.json +++ b/seed/challenges/json-apis-and-ajax.json @@ -7,7 +7,7 @@ "id": "bb000000000000000000001", "title": "Trigger Click Events with jQuery", "description": [ - "In this section, we'll learn how to get data from APIs. APIs - or Application Interfaces - are tools that computers use to communicate with one another.", + "In this section, we'll learn how to get data from APIs. APIs - or Application Programming Interfaces - are tools that computers use to communicate with one another.", "We'll also learn how to update HTML with the data we get from these APIs using a technology called Ajax.", "First, let's review what the $(document).ready() function does. This function makes it so all code inside of it only runs once our page loads.", "Let's make our \"Get Message\" button change the text of the element with the class message.", @@ -31,21 +31,21 @@ "", "", "

", - "
", - "

Cat Photo Finder

", - "
", - "
", - "
", - " The message will go here", - "
", - "
", - "
", - "
", - " ", - "
", - "
", + "
", + "

Cat Photo Finder

", + "
", + "
", + "
", + " The message will go here", + "
", + "
", + "
", + "
", + " ", + "
", + "
", "
" ], "challengeType": 0, @@ -68,30 +68,29 @@ " $(document).ready(function() {", " $(\"#getMessage\").on(\"click\", function(){", " // Only change code below this line.", - "", + " ", " // Only change code above this line.", " });", " });", "fcces", "", "", - "
", - "
", - "

Cat Photo Finder

", - "
", - "
", - "
", - " The message will go here", - "
", - "
", - "
", - "
", - " ", - "
", - "
", + "
", + "

Cat Photo Finder

", + "
", + "
", + "
", + " The message will go here", + "
", + "
", + "
", + "
", + " ", + "
", + "
", "
" ], "challengeType": 0, @@ -102,55 +101,55 @@ "title": "Get JSON with the jQuery getJSON Method", "description": [ "You can also request data from an external source. This is where APIs come into play.", - "Remember that APIs - or Application Interfaces - are tools that computers use to communicate with one another.", + "Remember that APIs - or Application Programming Interfaces - are tools that computers use to communicate with one another.", "Most web APIs transfer data in a format called JSON. JSON stands for JavaScript Object Notation.", "You've already been using JSON whenever you create a JavaScript object. JSON is nothing more than object properties and their current values, sandwiched between a { and a }.", "These properties and their values are often referred to as \"key-value pairs\".", "Let's get the JSON from Free Code Camp's Cat Photo API.", "Here's the code you can put in your click event to do this:", - "  $.getJSON(\"/json/cats.json?callback=\", function( json ) {", - "    $(\".message\").html(JSON.stringify(json))", + "  $.getJSON(\"/json/cats.json\", function(json) {", + "    $(\".message\").html(JSON.stringify(json));", "  });", "Once you've added this, click the \"Get Message\" button. Your Ajax function will replace the \"The message will go here\" text with the raw JSON output from the Free Code Camp Cat Photo API." ], "tests": [ "assert(editor.match(/\\$\\s*?\\(\\s*?(\\\"|\\')\\#getMessage(\\\"|\\')\\s*?\\)\\s*?\\.\\s*?on\\s*?\\(\\s*?(\\\"|\\')click(\\\"|\\')\\s*?\\,\\s*?function\\s*?\\(\\s*?\\)\\s*?\\{/gi), 'You should have a click handler on the getMessage button to trigger the AJAX request.')", - "assert(editor.match(/\\s*?\\}\\s*?\\)\\s*?\\;/gi), 'You should have at least on closing set of brackets and parenthesis.')", + "assert(editor.match(/\\s*?\\}\\s*?\\)\\s*?\\;/gi), 'You should have at least one closing set of brackets and parenthesis.')", "assert(editor.match(/\\s*?\\}\\s*?\\)\\s*?\\;/gi) && editor.match(/\\,\\s*?function\\s*?\\(\\s*?\\w*?\\s*?\\)\\s*?\\{/gi) && editor.match(/\\s*?\\}\\s*?\\)\\s*?\\;/gi).length === editor.match(/\\s*?function\\s*?\\(\\s*?\\w*?\\s*?\\)\\s*?\\{/gi).length, 'Each callback function should have a closing set of brackets and parenthesis.')", - "assert(editor.match(/\\$\\s*?\\.\\s*?getJSON\\s*?\\(\\s*?\"\\\/json\\\/cats\\.json\\?callback\\=\"\\s*?\\,\\s*?function\\s*?\\(\\s*?json\\s*?\\)\\s*?\\{/gi), 'You should be making use of the getJSON method given in the description to load data from the json file.')", - "assert(editor.match(/\\$\\s*?\\(\\s*?\\\"\\.message\\\"\\s*?\\)\\s*?\\.\\s*?html\\s*?\\(\\s*?JSON\\s*?\\.\\s*?stringify\\s*?\\(\\s*?json\\s*?\\)\\s*?\\)/gi), 'Don\\'t forget to make the .html change the contents of the message box so that it contains the result of the getJSON.')" + "assert(editor.match(/\\$\\s*?\\.\\s*?getJSON\\s*?\\(\\s*?(\\\"|\\')\\\/json\\\/cats\\.json(\\\"|\\')\\s*?\\,\\s*?function\\s*?\\(\\s*?json\\s*?\\)\\s*?\\{/gi), 'You should be making use of the getJSON method given in the description to load data from the json file.')", + "assert(editor.match(/\\$\\s*?\\(\\s*?(\\\"|\\')\\.message(\\\"|\\')\\s*?\\)\\s*?\\.\\s*?html\\s*?\\(\\s*?JSON\\s*?\\.\\s*?stringify\\s*?\\(\\s*?json\\s*?\\)\\s*?\\)/gi), 'Don\\'t forget to make the .html change the contents of the message box so that it contains the result of the getJSON.')" ], "challengeSeed": [ "fccss", - " $(document).ready(function() {", - " ", - " $(\"#getMessage\").on(\"click\", function(){", - " // Only change code below this line.", + " $(document).ready(function() {", "", + " $(\"#getMessage\").on(\"click\", function(){", + " // Only change code below this line.", + " ", + " ", + " ", + " // Only change code above this line.", + " });", "", - "", - " // Only change code above this line.", - " });", - " ", - " });", + " });", "fcces", "", "
", - "
", - "

Cat Photo Finder

", - "
", - "
", - "
", - " The message will go here", - "
", - "
", - "
", - "
", - " ", - "
", - "
", + "
", + "

Cat Photo Finder

", + "
", + "
", + "
", + " The message will go here", + "
", + "
", + "
", + "
", + " ", + "
", + "
", "
" ], "challengeType": 0, @@ -166,9 +165,12 @@ "Then, let's loop through our JSON, adding more HTML to that variable. When the loop is finished, we'll render it.", "Here's the code that does this:", "json.map(function(val) {", - "  html = html + \"<div class = 'cat'>\"", - "  html = html + '<div>' + val + '</div>';", - "  html = html + \"</div><br/>\"", + "  var keys = Object.keys(val);", + "  html += \"<div class = 'cat'>\";", + "  keys.map(function(key) {", + "    html += \"<b>\" + key + \"</b>: \" + val[key] + \"<br>\";", + "  });", + "  html += \"</div><br>\";", "});" ], "tests": [ @@ -179,18 +181,13 @@ " $(document).ready(function() {", "", " $(\"#getMessage\").on(\"click\", function() {", - "   $.getJSON(\"/json/cats.json?callback=\", function( json ) {", + "   $.getJSON(\"/json/cats.json\", function(json) {", "", + " var html = \"\";", " // Only change code below this line.", - "", - "", - "", - "", - "", - "", - "", - "", - "", + " ", + " ", + " ", " // Only change code above this line.", "", "     $(\".message\").html(html);", @@ -201,20 +198,20 @@ "fcces", "", "
", - "
", - "

Cat Photo Finder

", - "
", - "
", - "
", - " The message will go here","
", - "
", - "
", - "
", - " ", - "
", - "
", + "
", + "

Cat Photo Finder

", + "
", + "
", + "
", + " The message will go here","
", + "
", + "
", + "
", + " ", + "
", + "
", "
" ], "challengeType": 0, @@ -225,35 +222,34 @@ "title": "Render Images from Data Sources", "description": [ "In the JSON that we receive data from Free Code Camp's Cat Photo API.", - "When we're looping through these strings, let's check whether they are links. If it is, instead of outputing the image link, let's render the image.", + "We've seen from the last two lessons that each object in our JSON array contains an imageLink key with a value that is the url of a cat's image.", + "When we're looping through these objects, let's use this imageLink property to display this image in an img element.", "Here's the code that does this:", - "if(val.match(\"http\")) {", - "  html = html + '<img src = \"' + val + '\">';", - "}" + "  html += \"<img src = '\" + val.imageLink + \"'>\";" ], "tests": [ - "assert(editor.match(/val.match/gi), 'You should have checked whether the strings contain links.')" + "assert(editor.match(/val.imageLink/gi), 'You should have used the imageLink property to display the images.')" ], "challengeSeed": [ "fccss", " $(document).ready(function() {", "", " $(\"#getMessage\").on(\"click\", function() {", - "  $.getJSON(\"/json/cats.json?callback=\", function( json ) {", + "  $.getJSON(\"/json/cats.json\", function(json) {", "", " var html = \"\";", "", " json.map(function(val) {", "", - " html = html + \"
\"", + " html += \"
\";", "", - " // Only change code below this line.", + " // Only change code below this line.", + " ", + " ", + " ", + " // Only change code above this line.", "", - "", - "", - " // Only change code above this line.", - "", - " html = html + \"
\"", + " html += \"
\";", "", " });", "", @@ -265,21 +261,21 @@ "fcces", "", "
", - "
", - "

Cat Photo Finder

", - "
", - "
", - "
", - " The message will go here", - "
", - "
", - "
", - "
", - " ", - "
", - "
", + "
", + "

Cat Photo Finder

", + "
", + "
", + "
", + " The message will go here", + "
", + "
", + "
", + "
", + " ", + "
", + "
", "
" ], "challengeType": 0, @@ -304,25 +300,23 @@ " $(document).ready(function() {", "", " $(\"#getMessage\").on(\"click\", function() {", - "   $.getJSON(\"/json/cats.json?callback=\", function( json ) {", + "   $.getJSON(\"/json/cats.json\", function(json) {", "", " var html = \"\";", "", " // Only change code below this line.", - "", - "", - "", + " ", + " ", + " ", " // Only change code above this line.", "", " json.map(function(val){", "", - "    val = \"\" ", + " html += \"
\"", "", - " html = html + \"
\"", + " html += \"\"", "", - " html = html + '
' + val + '
';", - "", - " html = html + \"
\"", + " html += \"
\"", "", " });", "", @@ -333,25 +327,23 @@ " });", "fcces", "", - "", "
", - "
", - "

Cat Photo Finder

", - "
", - "
", - "
", - " The message will go here", - "
", - "
", - "
", - "
", - " ", - "
", - "
", - "
", - "" + "
", + "

Cat Photo Finder

", + "
", + "
", + "
", + " The message will go here", + "
", + "
", + "
", + "
", + " ", + "
", + "
", + "" ], "challengeType": 0, "type": "waypoint" @@ -375,9 +367,9 @@ "challengeSeed": [ "fccss", " // Only change code below this line.", - "", - "", - "", + " ", + " ", + " ", " // Only change code above this line.", "fcces", "
", From 9f35b9d61916f8ea04c46d84d650fe4365adcd33 Mon Sep 17 00:00:00 2001 From: natac13 Date: Fri, 30 Oct 2015 19:13:31 -0400 Subject: [PATCH 10/33] added warning about navigator prompt closes #3996 --- seed/challenges/json-apis-and-ajax.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seed/challenges/json-apis-and-ajax.json b/seed/challenges/json-apis-and-ajax.json index 940cb95bdb..6c44cabac4 100644 --- a/seed/challenges/json-apis-and-ajax.json +++ b/seed/challenges/json-apis-and-ajax.json @@ -354,6 +354,8 @@ "description": [ "Another cool thing we can do is access our user's current location. Every browser has a built in navigator that can give us this information.", "The navigator will get our user's current longitude and latitude.", + "You will see a prompt to allow or block this site from knowing your current location. The challenge can be completed either way, as long as the code is correct.", + "By selecting allow you will see the text on the output phone change to your latitude and longitude", "Here's some code that does this:", "if (navigator.geolocation) {", "  navigator.geolocation.getCurrentPosition(function(position) {", From ceecb086103355dcfd1d1632f8fceb41967806cb Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Fri, 30 Oct 2015 22:00:47 -0700 Subject: [PATCH 11/33] Fix BF Title Case Test Closes #3366 --- seed/challenges/basic-bonfires.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json index f81241c9d2..8cef5e19ca 100644 --- a/seed/challenges/basic-bonfires.json +++ b/seed/challenges/basic-bonfires.json @@ -243,8 +243,8 @@ "titleCase(\"I'm a little tea pot\");" ], "tests": [ - "assert(typeof(titleCase(\"I'm a little tea pot\")) === \"string\", 'message: titleCase() should return a string.');", - "assert(titleCase(\"I'm a little tea pot\") === \"I'm A Little Tea Pot\", 'message: titleCase(\"I'm a little tea pot\") should return \"I'm A Little Tea Pot\".');", + "assert(typeof(titleCase(\"I'm a little tea pot\")) === \"string\", 'message: titleCase() should return a string.');", + "assert(titleCase(\"I'm a little tea pot\") === \"I'm A Little Tea Pot\", 'message: titleCase(\"I'm a little tea pot\") should return \"I'm A Little Tea Pot\".');", "assert(titleCase(\"sHoRt AnD sToUt\") === \"Short And Stout\", 'message: titleCase(\"sHoRt AnD sToUt\") should return \"Short And Stout\".');", "assert(titleCase(\"HERE IS MY HANDLE HERE IS MY SPOUT\") === \"Here Is My Handle Here Is My Spout\", 'message: titleCase(\"HERE IS MY HANDLE HERE IS MY SPOUT\") should return \"Here Is My Handle Here Is My Spout\".');" ], From 03f189b2607dc4dc500685f7d8cd63e5c6037b9f Mon Sep 17 00:00:00 2001 From: Quincy Larson Date: Fri, 30 Oct 2015 23:39:11 -0700 Subject: [PATCH 12/33] add reddit add and update landing map stats --- client/less/main.less | 6 +++ server/boot/challenge.js | 1 + server/views/challengeMap/show.jade | 57 +++++++++++++++++------------ 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/client/less/main.less b/client/less/main.less index aa70867894..31da81da7e 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -689,6 +689,12 @@ iframe.iphone { transition: background .2s ease-in-out, border .2s ease-in-out; } +.population-table { + @media (max-width: 767px) { + font-size: 16px; + } +} + @media (max-width: 991px) { .navbar-header { float: none; diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 13e0563589..4a6aeeecf4 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -579,6 +579,7 @@ module.exports = function(app) { res.render('challengeMap/show', { blocks, daysRunning, + globalCompletedCount: numberWithCommas(5612952 + (Math.floor((Date.now() - 1446268581061) / 3000))), camperCount, lastCompleted, title: "A map of all Free Code Camp's Challenges" diff --git a/server/views/challengeMap/show.jade b/server/views/challengeMap/show.jade index c33348c259..5b4974b4e3 100644 --- a/server/views/challengeMap/show.jade +++ b/server/views/challengeMap/show.jade @@ -10,27 +10,36 @@ block content else img.img-responsive.img-center.border-radius-5(src='https://s3.amazonaws.com/freecodecamp/wide-social-banner.png') .col-xs-12.col-md-8.col-md-offset-2 - h2.text-center - span.text-primary #{camperCount}   - | campers have joined our community - br - | since we launched   - span.text-primary #{daysRunning}   - | days ago. + h2 + table.population-table.img-center + tr + td Established:  + td + span.text-primary #{daysRunning}  + | days ago + tr + td Population:  + td + span.text-primary #{camperCount}  + | campers + tr + td Completed:  + td + span.text-primary #{globalCompletedCount}  + | challenges + + + .spacer - if (user && user.progressTimestamps.length > 5) - #tshirt-notice.col-xs-12.col-md-8.col-md-offset-2.hidden - h2.text-center Get our first-edition t-shirt. - br - span.text-success Only available until Oct 29! - img.thumbnail.img-center.img-responsive(src="http://i.imgur.com/o07uuOL.png") - p.text-justify Our community has voted. Get our winning design emblazoned on a durable, American-made American Apparel shirt (available in women's and men's sizes).  - a(href="https://teespring.com/free-code-camp-shirt-eu" target="_blank") Also ships from Europe - | . - a.button.btn.btn-block.signup-btn(href="https://teespring.com/get-free-code-camp-t-shirt" target="_blank") Get yours + if (user && user.progressTimestamps.length > 100) + #map-notice.col-xs-12.col-md-8.col-md-offset-2.hidden + h2.text-center We have a subreddit + img.thumbnail.img-center.img-responsive(src="http://i.imgur.com/lyd0bfM.jpg") + h4.text-center We welcome you to come ask questions and share your thoughts with our entire open source community. + a.button.btn.btn-block.btn-primary(href="https://reddit.com/r/freecodecamp" target="_blank") Check it out .button-spacer .text-center - a#hideTshirtNoticeButton(href='#') Hide this forever + a#hide-map-notice-button(href='#') Hide this .spacer .row .col-xs-12.col-sm-8.col-sm-offset-2 @@ -131,14 +140,14 @@ block content var username = !{JSON.stringify(user && user.username || '')}; var lastCompleted = !{JSON.stringify(lastCompleted || false)} $(document).ready(function () { - if (!localStorage || !localStorage.hideTshirtNotice) { - $("#tshirt-notice").removeClass("hidden"); + if (!localStorage || !localStorage.hideRedditNotice) { + $("#map-notice").removeClass("hidden"); } - $("#hideTshirtNoticeButton").on("click", function() { - $("#tshirt-notice").addClass('animated fadeOut'); + $("#hide-map-notice-button").on("click", function() { + $("#map-notice").addClass('animated fadeOut'); setTimeout(function() { - $("#tshirt-notice").hide(); + $("#map-notice").hide(); }, 1000); - localStorage.hideTshirtNotice = "true"; + localStorage.hideRedditNotice = "true"; }); }); From 0fefac109059784d01cad3b453d40de9a9f651aa Mon Sep 17 00:00:00 2001 From: Quincy Larson Date: Sat, 31 Oct 2015 02:59:09 -0700 Subject: [PATCH 13/33] update page titles and sitemap --- server/boot/challenge.js | 2 +- server/boot/home.js | 2 +- server/boot/labs.js | 0 server/boot/randomAPIs.js | 13 +++-------- server/boot/story.js | 2 +- server/boot/user.js | 10 ++++---- server/views/resources/sitemap.jade | 36 ++++++++++++++++++++++++----- 7 files changed, 41 insertions(+), 24 deletions(-) delete mode 100644 server/boot/labs.js diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 13e0563589..3bfccf86cc 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -581,7 +581,7 @@ module.exports = function(app) { daysRunning, camperCount, lastCompleted, - title: "A map of all Free Code Camp's Challenges" + title: "A Map to Learn to Code and Become a Software Engineer" }); }, next diff --git a/server/boot/home.js b/server/boot/home.js index 511c9aedf4..0177fa1edc 100644 --- a/server/boot/home.js +++ b/server/boot/home.js @@ -1,7 +1,7 @@ import { defaultProfileImage } from '../../common/utils/constantStrings.json'; const message = - 'Learn to Code and Build Projects for Nonprofits'; + 'Learn to Code and Help Nonprofits'; module.exports = function(app) { var router = app.loopback.Router(); diff --git a/server/boot/labs.js b/server/boot/labs.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js index f15cbde478..997731055a 100644 --- a/server/boot/randomAPIs.js +++ b/server/boot/randomAPIs.js @@ -28,7 +28,6 @@ module.exports = function(app) { router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm); router.get('/nonprofits', nonprofits); router.get('/nonprofits-form', nonprofitsForm); - router.get('/our-sponsors', sponsors); router.get('/unsubscribe/:email', unsubscribe); router.get('/unsubscribed', unsubscribed); router.get('/get-started', getStarted); @@ -185,14 +184,14 @@ module.exports = function(app) { function showLabs(req, res) { res.render('resources/labs', { - title: 'Projects Built by Free Code Camp Students', + title: 'Projects Built by Free Code Camp Software Engineers', projects: labs }); } function showTestimonials(req, res) { res.render('resources/stories', { - title: 'Stories from Happy Free Code Camp Campers', + title: 'Testimonials from Happy Free Code Camp Students who got Software Engineer Jobs', stories: testimonials }); } @@ -207,15 +206,9 @@ module.exports = function(app) { }); } - function sponsors(req, res) { - res.render('sponsors/sponsors', { - title: 'The Sponsors who make Free Code Camp Possible' - }); - } - function nonprofits(req, res) { res.render('resources/nonprofits', { - title: 'A guide to our Nonprofit Projects' + title: 'Your Nonprofit Can Get Pro Bono Code' }); } diff --git a/server/boot/story.js b/server/boot/story.js index c1f188db81..a883a30280 100755 --- a/server/boot/story.js +++ b/server/boot/story.js @@ -109,7 +109,7 @@ module.exports = function(app) { function hot(req, res) { return res.render('stories/index', { - title: 'Hot stories currently trending on Camper News', + title: 'Top Stories on Camper News', page: 'hot' }); } diff --git a/server/boot/user.js b/server/boot/user.js index 3dac4a9a19..d1d7df35f3 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -128,7 +128,7 @@ module.exports = function(app) { return res.redirect('/'); } res.render('account/signin', { - title: 'Free Code Camp Login' + title: 'Sign in to Free Code Camp using a Social Media Account' }); } @@ -142,7 +142,7 @@ module.exports = function(app) { return res.redirect('/'); } res.render('account/email-signin', { - title: 'Sign in to your Free Code Camp Account' + title: 'Sign in to Free Code Camp using your Email Address' }); } @@ -151,7 +151,7 @@ module.exports = function(app) { return res.redirect('/'); } res.render('account/email-signup', { - title: 'Create Your Free Code Camp Account' + title: 'Sign up for Free Code Camp using your Email Address' }); } @@ -222,7 +222,7 @@ module.exports = function(app) { }); res.render('account/show', { - title: 'Camper ' + profileUser.username + '\'s portfolio', + title: 'Camper ' + profileUser.username + '\'s Code Portfolio', username: profileUser.username, name: profileUser.name, @@ -397,7 +397,7 @@ module.exports = function(app) { return res.render('account/forgot'); } res.render('account/reset', { - title: 'Password Reset', + title: 'Reset your Password', accessToken: req.accessToken.id }); } diff --git a/server/views/resources/sitemap.jade b/server/views/resources/sitemap.jade index edd0367e6a..6b4907a199 100644 --- a/server/views/resources/sitemap.jade +++ b/server/views/resources/sitemap.jade @@ -26,17 +26,41 @@ urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9") priority= 0.9 url - loc http://www.freecodecamp.com/coding-bootcamp-cost-calculator + loc http://www.freecodecamp.com/labs changefreq weekly lastmod= now priority= 0.9 + url + loc http://www.freecodecamp.com/stories + changefreq weekly + lastmod= now + priority= 0.9 + + url + loc http://www.freecodecamp.com/twitch + changefreq weekly + lastmod= now + priority= 0.9 + + url + loc http://www.freecodecamp.com/jobs + changefreq weekly + lastmod= now + priority= 0.9 + + url + loc http://www.freecodecamp.com/coding-bootcamp-cost-calculator + changefreq monthly + lastmod= now + priority= 0.9 + //- User each user in users url loc #{appUrl}/#{user} lastmod= now - changefreq daily + changefreq weekly priority= 0.5 each challenge in challenges @@ -44,18 +68,18 @@ urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9") loc #{appUrl}/challenges/#{challenge.replace(/\s/g, '-')} lastmod= now changefreq weekly - priority= 0.5 + priority= 0.9 each story in stories url loc #{appUrl}/news/#{story.replace(/\s/g, '-')} lastmod= now - changefreq daily - priority= 0.9 + changefreq weekly + priority= 0.5 each nonprofit in nonprofits url loc #{appUrl}/nonprofits/#{nonprofit.replace(/\s/g, '-')} lastmod= now - changefreq daily + changefreq weekly priority= 0.9 From e9971f4ea0b12ef44c0e0410b0394d66ec677d58 Mon Sep 17 00:00:00 2001 From: Vladimir Tamara Date: Fri, 30 Oct 2015 23:49:11 -0500 Subject: [PATCH 14/33] Points to working example of using Twitch JSONP --- seed/challenges/intermediate-ziplines.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/intermediate-ziplines.json b/seed/challenges/intermediate-ziplines.json index bc8c20c063..454a7eb72f 100644 --- a/seed/challenges/intermediate-ziplines.json +++ b/seed/challenges/intermediate-ziplines.json @@ -64,7 +64,7 @@ "User Story: As a user, if Free Code Camp is streaming, I can see additional details about what they are streaming.", "Bonus User Story: As a user, I can search through the streams listed.", "Bonus User Story: As a user, I will see a placeholder notification if a streamer has closed their Twitch account. You can verify this works by adding brunofin and comster404 to your array of Twitch streamers.", - "Hint: Here's an example call to Twitch.tv's JSON API: https://api.twitch.tv/kraken/streams/freecodecamp.", + "Hint: See an example call to Twitch.tv's JSONP API at https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Zipline-Use-the-Twitchtv-JSON-API.", "Hint: The relevant documentation about this API call is here: https://github.com/justintv/Twitch-API/blob/master/v3_resources/streams.md#get-streamschannel.", "Hint: Here's an array of the Twitch.tv usernames of people who regularly stream coding: [\"freecodecamp\", \"storbeck\", \"terakilobyte\", \"habathcx\",\"RobotCaleb\",\"thomasballinger\",\"noobs2ninjas\",\"beohoff\"]", "Remember to use Read-Search-Ask if you get stuck.", From ab83f6d5fa6ec2ea143b807b8ecb40fbe7c889d0 Mon Sep 17 00:00:00 2001 From: Arsen Melikyan Date: Sat, 31 Oct 2015 17:24:05 +0400 Subject: [PATCH 15/33] adds back button for step challenges --- client/commonFramework.js | 48 +++++++++++++++++++++++++- server/views/coursewares/showStep.jade | 4 +++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/client/commonFramework.js b/client/commonFramework.js index 2505fe08f4..39a40923b9 100644 --- a/client/commonFramework.js +++ b/client/commonFramework.js @@ -892,12 +892,32 @@ var runTests = function(err, data) { // step challenge common.init.push((function() { var stepClass = '.challenge-step'; + var prevBtnClass = '.challenge-step-btn-prev'; var nextBtnClass = '.challenge-step-btn-next'; var actionBtnClass = '.challenge-step-btn-action'; var finishBtnClass = '.challenge-step-btn-finish'; var submitBtnId = '#challenge-step-btn-submit'; var submitModalId = '#challenge-step-modal'; + function getPreviousStep($challengeSteps) { + var length = $challengeSteps.length; + var $prevStep = false; + var prevStepIndex = 0; + $challengeSteps.each(function(index) { + var $step = $(this); + if ( + !$step.hasClass('hidden') && + index + 1 !== length + ) { + prevStepIndex = index - 1; + } + }); + + $prevStep = $challengeSteps[prevStepIndex]; + + return $prevStep; + } + function getNextStep($challengeSteps) { var length = $challengeSteps.length; var $nextStep = false; @@ -917,11 +937,36 @@ common.init.push((function() { return $nextStep; } + function handlePrevStepClick(e) { + e.preventDefault(); + var prevStep = getPreviousStep($(stepClass)); + $(this) + .parent() + .removeClass('fadeOutLeft') + .addClass('animated fadeOutRight fast-animation') + .delay(250) + .queue(function(prev) { + $(this).addClass('hidden'); + if (prevStep) { + $(prevStep) + .removeClass('hidden') + .removeClass('slideInRight') + .addClass('animated slideInLeft fast-animation') + .delay(500) + .queue(function(prev) { + prev(); + }); + } + prev(); + }); + } + function handleNextStepClick(e) { e.preventDefault(); var nextStep = getNextStep($(stepClass)); $(this) .parent() + .removeClass('fadeOutRight') .addClass('animated fadeOutLeft fast-animation') .delay(250) .queue(function(next) { @@ -929,10 +974,10 @@ common.init.push((function() { if (nextStep) { $(nextStep) .removeClass('hidden') + .removeClass('slideInLeft') .addClass('animated slideInRight fast-animation') .delay(500) .queue(function(next) { - $(this).removeClass('slideInRight'); next(); }); } @@ -1035,6 +1080,7 @@ common.init.push((function() { } return function($) { + $(prevBtnClass).click(handlePrevStepClick); $(nextBtnClass).click(handleNextStepClick); $(actionBtnClass).click(handleActionClick); $(finishBtnClass).click(handleFinishClick); diff --git a/server/views/coursewares/showStep.jade b/server/views/coursewares/showStep.jade index 74bb6705f5..16e4777c54 100644 --- a/server/views/coursewares/showStep.jade +++ b/server/views/coursewares/showStep.jade @@ -10,8 +10,12 @@ block content p.large-p!= step[2] if step[3] a.btn.btn-block.btn-primary.challenge-step-btn-action(id='#{index}' href='#{step[3]}' target='_blank') Open link in new tab + .button-spacer if index + 1 === description.length .btn.btn-block.btn-primary.challenge-step-btn-finish(id='last' class=step[3] ? 'disabled' : '') Finish challenge + else if index !== 0 + .col-xs-12.col-md-6.col-lg-6.btn.btn-danger.challenge-step-btn-prev(id='#{index - 1}' class=step[3] ? 'disabled' : '') Go to my previous step + .col-xs-12.col-md-6.col-lg-6.btn.btn-primary.challenge-step-btn-next(id='#{index}' class=step[3] ? 'disabled' : '') Go to my next step else .btn.btn-block.btn-primary.challenge-step-btn-next(id='#{index}' class=step[3] ? 'disabled' : '') Go to my next step #challenge-step-modal.modal(tabindex='-1') From bda6495e27e3f1e6a2c1d9f7135a4ab778813097 Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Sat, 31 Oct 2015 09:25:17 -0700 Subject: [PATCH 16/33] Fix Waypoint Target Parent Seed Code Closes #4013 --- seed/challenges/jquery.json | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/seed/challenges/jquery.json b/seed/challenges/jquery.json index 37a0899018..f4bdcbd520 100644 --- a/seed/challenges/jquery.json +++ b/seed/challenges/jquery.json @@ -645,27 +645,29 @@ "", "", "", - "
", - "

jQuery Playground

", - "
", - "
", - "

#left-well

", - "
", - " ", - " ", - " ", + "", + "
", + "

jQuery Playground

", + "
", + "
", + "

#left-well

", + "
", + " ", + " ", + " ", + "
", "
", - "
", - "
", - "

#right-well

", - "
", - " ", - " ", - " ", + "
", + "

#right-well

", + "
", + " ", + " ", + " ", + "
", "
", "
", "
", - "
" + "" ], "type": "waypoint", "challengeType": 0 From a369e6db92780b4af1a22834de681aaa11b05b71 Mon Sep 17 00:00:00 2001 From: krantzinator Date: Sat, 31 Oct 2015 13:26:35 -0400 Subject: [PATCH 17/33] update test to allow space between css property and colon --- seed/challenges/html5-and-css.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/html5-and-css.json b/seed/challenges/html5-and-css.json index 9ae6a4ad8b..82f2c72603 100644 --- a/seed/challenges/html5-and-css.json +++ b/seed/challenges/html5-and-css.json @@ -390,7 +390,7 @@ "assert(!$(\"h2\").attr(\"style\"), 'Remove the style attribute from your h2 element.')", "assert($(\"style\") && $(\"style\").length > 1, 'Create a style element.')", "assert($(\"h2\").css(\"color\") === \"rgb(0, 0, 255)\", 'Your h2 element should be blue.')", - "assert(editor.match(/h2\\s*\\{\\s*color:\\s*blue;\\s*\\}/g), 'Ensure that your stylesheet h2 declaration is valid with a semicolon and closing brace')", + "assert(editor.match(/h2\\s*\\{\\s*color\\s*:\\s*blue;\\s*\\}/g), 'Ensure that your stylesheet h2 declaration is valid with a semicolon and closing brace')", "assert(editor.match(/<\\/style>/g) && editor.match(/<\\/style>/g).length === (editor.match(//g) || []).length, 'Make sure all your style elements are valid and have a closing tag.')" ], "challengeSeed": [ From 8e4244fcefefc04dc12f7661b9b4f4ef673a4a77 Mon Sep 17 00:00:00 2001 From: natac13 Date: Sat, 31 Oct 2015 17:42:01 -0400 Subject: [PATCH 18/33] fix seed different from description iterating odd numbers with for loop closes #4081 --- seed/challenges/basic-javascript.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index c4f5cd3e0a..a871f43675 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -925,7 +925,7 @@ "challengeSeed":[ "var ourArray = [];", "", - "for(var i = 1; i < 10; i += 2){", + "for(var i = 0; i < 10; i += 2){", " ourArray.push(i);", "}", "", From e5449ee65fa3a8812b4f126ef04b56de16a4fc68 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 31 Oct 2015 22:13:18 -0700 Subject: [PATCH 19/33] Fix linting errors --- server/boot/challenge.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 4a6aeeecf4..a4d057f13b 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -35,12 +35,14 @@ const dasherize = utils.dasherize; const unDasherize = utils.unDasherize; const getMDNLinks = utils.getMDNLinks; +/* function makeChallengesUnique(challengeArr) { // clone and reverse challenges // then filter by unique id's // then reverse again return _.uniq(challengeArr.slice().reverse(), 'id').reverse(); } +*/ function numberWithCommas(x) { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } @@ -556,7 +558,7 @@ module.exports = function(app) { dashedName: dasherize(blockArray[0].block), challenges: blockArray, completed: completedCount / blockArray.length * 100, - time: blockArray[0] && blockArray[0].time || "???" + time: blockArray[0] && blockArray[0].time || '???' }; }) .filter(({ name }) => name !== 'Hikes') @@ -579,7 +581,9 @@ module.exports = function(app) { res.render('challengeMap/show', { blocks, daysRunning, - globalCompletedCount: numberWithCommas(5612952 + (Math.floor((Date.now() - 1446268581061) / 3000))), + globalCompletedCount: numberWithCommas( + 5612952 + (Math.floor((Date.now() - 1446268581061) / 3000)) + ), camperCount, lastCompleted, title: "A map of all Free Code Camp's Challenges" From f7530701ca31a40175ea054d2fd28372a9468506 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 31 Oct 2015 22:48:42 -0700 Subject: [PATCH 20/33] Change step challenge button layout --- client/less/main.less | 12 ++++++++++++ server/views/coursewares/showStep.jade | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/client/less/main.less b/client/less/main.less index 31da81da7e..f5c23c4e48 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -689,6 +689,18 @@ iframe.iphone { transition: background .2s ease-in-out, border .2s ease-in-out; } +.btn-warning-ghost { + background: transparent; + color: @brand-warning; + + /* CSS Transition */ + -webkit-transition: background .2s ease-in-out, border .2s ease-in-out; + -moz-transition: background .2s ease-in-out, border .2s ease-in-out; + -ms-transition: background .2s ease-in-out, border .2s ease-in-out; + -o-transition: background .2s ease-in-out, border .2s ease-in-out; + transition: background .2s ease-in-out, border .2s ease-in-out; +} + .population-table { @media (max-width: 767px) { font-size: 16px; diff --git a/server/views/coursewares/showStep.jade b/server/views/coursewares/showStep.jade index 16e4777c54..b87811cb80 100644 --- a/server/views/coursewares/showStep.jade +++ b/server/views/coursewares/showStep.jade @@ -14,8 +14,8 @@ block content if index + 1 === description.length .btn.btn-block.btn-primary.challenge-step-btn-finish(id='last' class=step[3] ? 'disabled' : '') Finish challenge else if index !== 0 - .col-xs-12.col-md-6.col-lg-6.btn.btn-danger.challenge-step-btn-prev(id='#{index - 1}' class=step[3] ? 'disabled' : '') Go to my previous step - .col-xs-12.col-md-6.col-lg-6.btn.btn-primary.challenge-step-btn-next(id='#{index}' class=step[3] ? 'disabled' : '') Go to my next step + .btn.btn-block.btn-primary.challenge-step-btn-next(id='#{index}' class=step[3] ? 'disabled' : '') Go to my next step + .btn.btn-block.btn-warning-ghost.challenge-step-btn-prev(id='#{index - 1}') Go to my previous step else .btn.btn-block.btn-primary.challenge-step-btn-next(id='#{index}' class=step[3] ? 'disabled' : '') Go to my next step #challenge-step-modal.modal(tabindex='-1') From 763747f494391fcdaa0dbbad15923e8863ff74c3 Mon Sep 17 00:00:00 2001 From: natac13 Date: Sat, 31 Oct 2015 18:03:21 -0400 Subject: [PATCH 21/33] fix unclear description and lack of usage in the editor. closes #4059 removed added code in the seed --- .../object-oriented-and-functional-programming.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seed/challenges/object-oriented-and-functional-programming.json b/seed/challenges/object-oriented-and-functional-programming.json index 4570dd45ff..3be9cb4a20 100644 --- a/seed/challenges/object-oriented-and-functional-programming.json +++ b/seed/challenges/object-oriented-and-functional-programming.json @@ -189,8 +189,8 @@ "title":"Iterate over Arrays with .map", "description":[ "The map method is a convenient way to iterate through arrays. Here's an example usage:", - "var timesFour = array.map(function(val){", - "  return val*4;", + "var timesFour = oldArray.map(function(val){", + "  return val * 4;", "});", "", "The map method will iterate through every element of the array, creating a new array with values that have been modified by the callback function, and return it.", From da02e74af64d0761d61d74d1a4ec90372b98bffc Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Sun, 1 Nov 2015 09:31:06 -0800 Subject: [PATCH 22/33] Fix Waypoint Random Numbers in Range Wording Closes #3997 --- seed/challenges/basic-javascript.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index a871f43675..72108c52b8 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -1093,7 +1093,7 @@ "Here's the formula we'll use. Take a moment to read it and try to understand what this code is doing:", "Math.floor(Math.random() * (max - min + 1)) + min", "Define two variables: myMin and myMax, and set them both equal to numbers.", - "Then create a function called myFunction that returns a random number that's greater than or equal to myMin, and is less than myMax." + "Then create a function called myFunction that returns a random number that's greater than or equal to myMin, and is less than or equal to myMax." ], "tests": [ "assert(myFunction() >= myMin, 'message: The random number generated by myFunction should be greater than or equal to your minimum number, myMin.');", From bde6b81b83e6aa96794d15f576e332906529bd83 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sun, 1 Nov 2015 10:00:38 -0800 Subject: [PATCH 23/33] Remove old files --- seed/challenge-migration.js | 44 ------- seed/challengeMapping.json | 226 ------------------------------------ seed/jobs.json | 47 -------- seed/storyCleanup.js | 47 -------- seed/userMigration.js | 137 ---------------------- test/basic_routes.js | 45 ------- test/mocha.opts | 2 - 7 files changed, 548 deletions(-) delete mode 100644 seed/challenge-migration.js delete mode 100644 seed/challengeMapping.json delete mode 100644 seed/jobs.json delete mode 100644 seed/storyCleanup.js delete mode 100644 seed/userMigration.js delete mode 100644 test/basic_routes.js delete mode 100644 test/mocha.opts diff --git a/seed/challenge-migration.js b/seed/challenge-migration.js deleted file mode 100644 index 7ef359781f..0000000000 --- a/seed/challenge-migration.js +++ /dev/null @@ -1,44 +0,0 @@ -require('dotenv').load(); -var bonfires = require('./bonfires.json'), - app = require('../server/server'), - mongodb = require('mongodb'), - MongoClient = mongodb.MongoClient, - User = app.models.User, - UserIdentity = app.models.userIdentity, - oldUri='mongodb://localhost:27017/app30893198', - coursewares = require('./coursewares.json'); - -MongoClient.connect(oldUri, function(err, database) { - - database.collection('users').find({}).batchSize(20).toArray(function(err, users) { - if (users !== null && users.length !== 0) { - var mappedUserArray = users.map(function(user) { - Object.keys(user.profile).forEach(function(prop) { - user[prop] = user.profile[prop]; - }); - Object.keys(user.portfolio).forEach(function(prop) { - user[prop] = user.portfolio[prop]; - }); - - user.completedCoursewares = Object.keys(user.challengesHash) - .filter(function(key) { - return user.challengesHash[key] !== 0; - }) - .map(function(key) { - return({ - id: coursewares[key].id, - completedDate: user.challengesHash[key] - }); - }); - - return user; - }); - User.create(mappedUserArray, function(err) { - if (err) { - console.log(err); - } - console.log("a batch finished"); - }); - } - }); -}); diff --git a/seed/challengeMapping.json b/seed/challengeMapping.json deleted file mode 100644 index db6686d266..0000000000 --- a/seed/challengeMapping.json +++ /dev/null @@ -1,226 +0,0 @@ -[ - { - "oldNumber": "0", - "newId": "bd7124d8c441eddfaeb5bdef" - }, - { - "oldNumber": "1", - "newId": "bd7125d8c441eddfaeb5bd0f" - }, - { - "oldNumber": "2", - "newId": "" - }, - { - "oldNumber": "3", - "newId": "bd7127d8c441eddfaeb5bdef" - }, - { - "oldNumber": "4", - "newId": "bd7128d8c441eddfaeb5bdef" - }, - { - "oldNumber": "5", - "newId": "bd8129d8c441eddfaeb5bdef" - }, - { - "oldNumber": "6", - "newId": "" - }, - { - "oldNumber": "7", - "newId": "" - }, - { - "oldNumber": "8", - "newId": "bd7112d8c441eddfaeb5bdef" - }, - { - "oldNumber": "9", - "newId": "bd7113d8c441eddfaeb5bdef" - }, - { - "oldNumber": "10", - "newId": "bd7114d8c441eddfaeb5bdef" - }, - { - "oldNumber": "11", - "newId": "bd7115d8c441eddfaeb5bdef" - }, - { - "oldNumber": "12", - "newId": "bd7116d8c441eddfaeb5bdef" - }, - { - "oldNumber": "13", - "newId": "bd7117d8c441eddfaeb5bdef" - }, - { - "oldNumber": "14", - "newId": "bd7118d8c441eddfaeb5bdef" - }, - { - "oldNumber": "15", - "newId": "" - }, - { - "oldNumber": "16", - "newId": "" - }, - { - "oldNumber": "17", - "newId": "" - }, - { - "oldNumber": "18", - "newId": "" - }, - { - "oldNumber": "19", - "newId": "bd7123d8c441eddfaeb5bdef" - }, - { - "oldNumber": "20", - "newId": "bd8124d8c441eddfaeb5bdef" - }, - { - "oldNumber": "21", - "newId": "bd8126d8c441eddfaeb5bdef" - }, - { - "oldNumber": "22", - "newId": "bd8127d8c441eddfaeb5bdef" - }, - { - "oldNumber": "23", - "newId": "bd8128d8c441eddfaeb5bdef" - }, - { - "oldNumber": "24", - "newId": "bd7129d8c441eddfaeb5bdef" - }, - { - "oldNumber": "25", - "newId": "bd7130d8c441eddfaeb5bdef" - }, - { - "oldNumber": "26", - "newId": "bd7131d8c441eddfaeb5bdef" - }, - { - "oldNumber": "27", - "newId": "bd7132d8c441eddfaeb5bdef" - }, - { - "oldNumber": "28", - "newId": "bd7133d8c441eddfaeb5bdef" - }, - { - "oldNumber": "29", - "newId": "bd7134d8c441eddfaeb5bdef" - }, - { - "oldNumber": "30", - "newId": "bd7135d8c441eddfaeb5bdef" - }, - { - "oldNumber": "31", - "newId": "bd7136d8c441eddfaeb5bdef" - }, - { - "oldNumber": "32", - "newId": "" - }, - { - "oldNumber": "33", - "newId": "bd7138d8c441eddfaeb5bdef" - }, - { - "oldNumber": "34", - "newId": "bd7137d8c441eddfaeb5bdef" - }, - { - "oldNumber": "35", - "newId": "bd7140d8c441eddfaeb5bdef" - }, - { - "oldNumber": "36", - "newId": "" - }, - { - "oldNumber": "37", - "newId": "" - }, - { - "oldNumber": "38", - "newId": "" - }, - { - "oldNumber": "39", - "newId": "" - }, - { - "oldNumber": "40", - "newId": "" - }, - { - "oldNumber": "41", - "newId": "" - }, - { - "oldNumber": "42", - "newId": "" - }, - { - "oldNumber": "43", - "newId": "" - }, - { - "oldNumber": "44", - "newId": "" - }, - { - "oldNumber": "45", - "newId": "" - }, - { - "oldNumber": "46", - "newId": "" - }, - { - "oldNumber": "47", - "newId": "" - }, - { - "oldNumber": "48", - "newId": "bd7153d8c441eddfaeb5bd2f" - }, - { - "oldNumber": "49", - "newId": "bd7154d8c441eddfaeb5bdef" - }, - { - "oldNumber": "50", - "newId": "bd7155d8c441eddfaeb5bdef" - }, - { - "oldNumber": "51", - "newId": "bd7156d8c441eddfaeb5bdef" - }, - { - "oldNumber": "52", - "newId": "bd7157d8c441eddfaeb5bdef" - }, - { - "oldNumber": "53", - "newId": "bd7158d8c441eddfaeb5bdef" - }, - { - "oldNumber": "54", - "newId": "" - }, - { - "oldNumber": "55", - "newId": "" - } -] diff --git a/seed/jobs.json b/seed/jobs.json deleted file mode 100644 index 5850dc8aba..0000000000 --- a/seed/jobs.json +++ /dev/null @@ -1,47 +0,0 @@ -[ - { - "id": "bd7167d8c441cbafaeb5bdef", - "email": "Ada_Gerlach@gmail.com", - "phone": "1-140-557-0727", - "company": "Livestream", - "country": "Singapore", - "city": "Morar berg", - "state": "South Dakota", - "position": "Junior Backend Developer (Node.js)", - "logo": "https://s3.amazonaws.com/prod-heroku/greenhouse_job_boards/logos/000/001/189/resized/livestream_logo-rgb_standard-cc718e67ce1a0f6fa400f609bdefe605.png?1429547161", - "description": "Live life one inhalation and one exhalation at a time. May you be healthy. Let the muscles in your neck and shoulders relax. May you be safe. Reflect on the fragility and preciousness of life. Empty your mind; be formless, shapeless like water. May you be at peace. Take a deep breath. You can do what you set out to do; yes, you can. You can do what you set out to do; yes, you can. Watch each breath appear and disappear, just breathing. Give yourself a break. Live life one inhalation and one exhalation at a time. Give yourself a break. Just acknowledge what's there and let be. You can get through this. Open your heart to change, forgiveness and lovingkindness. Love is the first seed of the soul. It will be ok. Impermanence and change is a powerful teacher and teaching. Bring love into your heart, into your breath and into your being. Briefly notice any emotions, thoughts or sensations that may be driving fear and anxiety and let them be. This discomfort will pass. Bring love into your heart, into your breath and into your being. Take a deep breath. Reflect on the fragility and preciousness of life." - }, - { - "id": "bd7167d8c442cbafaeb5bdef", - "email": "Ada_Gerlach@gmail.com", - "company": "Adobe", - "country": "Singapore", - "city": "Morar berg", - "state": "South Dakota", - "position": "Junior JavaScript Engineer", - "logo": "http://cdn-3.famouslogos.us/images/adobe-logo.jpg", - "description": "Live life one inhalation and one exhalation at a time. May you be healthy. Let the muscles in your neck and shoulders relax. May you be safe. Reflect on the fragility and preciousness of life. Empty your mind; be formless, shapeless like water. May you be at peace. Take a deep breath. You can do what you set out to do; yes, you can. You can do what you set out to do; yes, you can. Watch each breath appear and disappear, just breathing. Give yourself a break. Live life one inhalation and one exhalation at a time. Give yourself a break. Just acknowledge what's there and let be. You can get through this. Open your heart to change, forgiveness and lovingkindness. Love is the first seed of the soul. It will be ok. Impermanence and change is a powerful teacher and teaching. Bring love into your heart, into your breath and into your being. Briefly notice any emotions, thoughts or sensations that may be driving fear and anxiety and let them be. This discomfort will pass. Bring love into your heart, into your breath and into your being. Take a deep breath. Reflect on the fragility and preciousness of life." - }, - { - "id": "bd7167d8c443cbafaeb5bdef", - "phone": "1-140-557-0727", - "company": "Bookspan", - "country": "Singapore", - "city": "Morar berg", - "state": "South Dakota", - "position": "Full Stack JavaScript Developer (Junior)", - "logo": "https://tm-prod.global.ssl.fastly.net/uploaded/companies/227/small_logo.png?v=db9dbe", - "description": "Live life one inhalation and one exhalation at a time. May you be healthy. Let the muscles in your neck and shoulders relax. May you be safe. Reflect on the fragility and preciousness of life. Empty your mind; be formless, shapeless like water. May you be at peace. Take a deep breath. You can do what you set out to do; yes, you can. You can do what you set out to do; yes, you can. Watch each breath appear and disappear, just breathing. Give yourself a break. Live life one inhalation and one exhalation at a time. Give yourself a break. Just acknowledge what's there and let be. You can get through this. Open your heart to change, forgiveness and lovingkindness. Love is the first seed of the soul. It will be ok. Impermanence and change is a powerful teacher and teaching. Bring love into your heart, into your breath and into your being. Briefly notice any emotions, thoughts or sensations that may be driving fear and anxiety and let them be. This discomfort will pass. Bring love into your heart, into your breath and into your being. Take a deep breath. Reflect on the fragility and preciousness of life." - }, - { - "id": "bd7167d8c444cbafaeb5bdef", - "email": "Ada_Gerlach@gmail.com", - "company": "Good Eggs", - "country": "Singapore", - "city": "Morar berg", - "state": "South Dakota", - "position": "Full Stack Developer", - "logo": "https://d1qb2nb5cznatu.cloudfront.net/startups/i/72165-64efbd521cdfe3357c811758f5436e7d-medium_jpg.jpg", - "description": "Live life one inhalation and one exhalation at a time. May you be healthy. Let the muscles in your neck and shoulders relax. May you be safe. Reflect on the fragility and preciousness of life. Empty your mind; be formless, shapeless like water. May you be at peace. Take a deep breath. You can do what you set out to do; yes, you can. You can do what you set out to do; yes, you can. Watch each breath appear and disappear, just breathing. Give yourself a break. Live life one inhalation and one exhalation at a time. Give yourself a break. Just acknowledge what's there and let be. You can get through this. Open your heart to change, forgiveness and lovingkindness. Love is the first seed of the soul. It will be ok. Impermanence and change is a powerful teacher and teaching. Bring love into your heart, into your breath and into your being. Briefly notice any emotions, thoughts or sensations that may be driving fear and anxiety and let them be. This discomfort will pass. Bring love into your heart, into your breath and into your being. Take a deep breath. Reflect on the fragility and preciousness of life." - } -] diff --git a/seed/storyCleanup.js b/seed/storyCleanup.js deleted file mode 100644 index 2598526c8a..0000000000 --- a/seed/storyCleanup.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Created by nathanleniz on 4/25/15. - */ -require('dotenv').load(); -var mongodb = require('mongodb'), - Story = require('../models/Story.js'), - secrets = require('../config/secrets'); - mongoose = require('mongoose'); - -mongoose.connect(secrets.db); - -function storyLinkCleanup(cb) { - console.log('headLineCleanup'); - var i = 1; - var stream = Story.find({}).skip(0).limit(0).batchSize(20000).stream(); - - stream.on('data', function (story) { - console.log(i++); - this.pause(); - story.storyLink = story.storyLink. - replace(/[^a-z0-9\s]/gi, ''). - replace(/\s+/g, ' '). - toLowerCase(). - trim(); - story.save(function (err) { - if (err) { - console.log('woops'); - } - this.resume(); - }.bind(this)); - }) - .on('error', function (err) { - console.error(err); - }).on('close', function () { - console.log('done with set'); - stream.destroy(); - cb(); - }); -} - -function done() { - console.log('Migration script has completed'); - process.exit(0); -} - - -storyLinkCleanup(done); diff --git a/seed/userMigration.js b/seed/userMigration.js deleted file mode 100644 index 350a57ffe3..0000000000 --- a/seed/userMigration.js +++ /dev/null @@ -1,137 +0,0 @@ -/*eslint-disable block-scoped-var */ -require('dotenv').load(); -var User = require('../models/User.js'), - secrets = require('../config/secrets'), - mongoose = require('mongoose'), - R = require('ramda'), - ziplines = require('./challenges/ziplines.json'), - basejumps = require('./challenges/basejumps.json'); - -mongoose.connect(secrets.db); - -var ziplineIds = ziplines.challenges.map(function(elem) { - return elem.id; -}); -var basejumpIds = basejumps.challenges.map(function(elem) { - return elem.id; -}); -var ziplineAndBaseJumpIds = R.concat(ziplineIds, basejumpIds); - -function userModelAssurity(cb) { - console.log('userModelAssurity'); - var i = 1; - var stream = User.find({}).skip(0).limit(0).batchSize(20000).stream(); - - stream.on('data', function (user) { - console.log(i++); - this.pause(); - user.needsMigration = true; - user.save(function (err) { - if (err) { - console.log('woops'); - } - this.resume(); - }.bind(this)); - }) - .on('error', function (err) { - console.log(err); - }).on('close', function () { - console.log('done with set'); - stream.destroy(); - cb(); - }); -} - -function migrateIt() { - console.log('migrateIt'); - var dones = 0; - var done = function() { - dones++; - if (dones === 2) { - process.exit(0); - } - if (dones === 1) { - userModelMigration(done); - } - }; - console.log('calling userModelAssurity'); - userModelAssurity(done); -} - -function userModelMigration(cb) { - - var i = 1; - - var stream = User.find({needsMigration: true}).skip(0).limit(0) - .batchSize(20000).stream(); - - stream.on('data', function (user) { - console.log(i++); - /* - if (user.challengesHash) { - this.pause(); - user.needsMigration = false; - var oldChallenges = Object.keys(user.challengesHash).filter(function (key) { - if (user.challengesHash[key]) { - user.progressTimestamps.push(user.challengesHash[key] * 1000); - } - return user.challengesHash[key]; - }); - - newChallenges.forEach(function (challenge) { - if (oldChallenges.indexOf(challenge.oldNumber) !== -1 && challenge.newId) { - user.completedCoursewares.push({ - id: challenge.newId, - completedDate: user.challengesHash[challenge.oldNumber] * 1000 - }); - } - }); - - user.completedCoursewares.forEach(function (course) { - var indexOfCourse = user.uncompletedCoursewares.indexOf(course.id) !== -1; - if (indexOfCourse !== -1) { - user.uncompletedCoursewares.splice(indexOfCourse, 1); - } - }); - - user.completedBonfires.forEach(function (bonfire) { - bonfire.completedDate = bonfire.completedDate * 1000; - user.progressTimestamps.push(bonfire.completedDate); - }); - } - */ - user.needsMigration = false; - user.completedChallenges = user.completedChallenges.map(function(elem) { - if (ziplineAndBaseJumpIds.indexOf(elem.id) > 0) { - return ({ - id: elem.id, - name: elem.name, - completedWith: elem.completedWith, - completedDate: elem.completedDate, - solution: elem.solution, - githubLink: elem.githubLink, - verified: elem.verified, - challengeType: typeof elem.githubLink === 'boolean' ? 4 : 3 - }); - } else { - return elem; - } - }); - - var self = this; - user.save(function (err) { - if (err) { - console.log('woops'); - } - self.resume(); - }); - }).on('error', function (err) { - console.log(err); - }).on('close', function () { - console.log('done with set'); - stream.destroy(); - cb(); - }); -} - -migrateIt(); diff --git a/test/basic_routes.js b/test/basic_routes.js deleted file mode 100644 index ac7d190787..0000000000 --- a/test/basic_routes.js +++ /dev/null @@ -1,45 +0,0 @@ -var request = require('supertest'); -var app = require('../server/server.js'); - -describe('#ROUTES', function () { - - describe('GET /', function () { - it('should return 200 OK', (done) => { - request(app) - .get('/') - .expect(200, done); - }); - }); - - describe('GET /signin', function () { - it('should return 200 OK', (done) => { - request(app) - .get('/signin') - .expect(200, done); - }); - }); - - describe('GET /email-signup', function () { - it('should return 200 OK', (done) => { - request(app) - .get('/email-signup') - .expect(200, done); - }); - }); - - describe('GET /random-url', function () { - it('should return 302', (done) => { - request(app) - .get('/reset') - .expect(302, done); - }); - }); - - describe('GET /camperName', function () { - it('should return 200', (done) => { - request(app) - .get('/terakilobyte') - .expect(200, done); - }); - }); -}); diff --git a/test/mocha.opts b/test/mocha.opts deleted file mode 100644 index 097dfe9134..0000000000 --- a/test/mocha.opts +++ /dev/null @@ -1,2 +0,0 @@ ---reporter spec ---timeout 5000 \ No newline at end of file From 9c9e1af8c11cdc031da6537a0a4368f7de7906bf Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Sun, 1 Nov 2015 10:06:22 -0800 Subject: [PATCH 24/33] Fix Sift Through Test With Regex Wording Closes #3898 --- seed/challenges/basic-javascript.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index a871f43675..30663b5836 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -1183,7 +1183,7 @@ "i means that we want to ignore the case (uppercase or lowercase) when searching for the pattern.", "Regular expressions are written by surrounding the pattern with / symbols.", "Let's try selecting all the occurrences of the word and in the string Ada Lovelace and Charles Babbage designed the first computer and the software that would have run on it.", - "We can do this by replacing the . part of our regular expression with the current regular expression with the word and." + "We can do this by replacing the . part of our regular expression with the word and." ], "tests": [ "assert(test==2, 'message: Your regular expression should find two occurrences of the word and.');", From a44f864e6f6778c92d906ea04f10b50f01b13b4b Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Sun, 1 Nov 2015 10:48:29 -0800 Subject: [PATCH 25/33] Fix Waypoint Unordered List Instructions Closes #3891 --- seed/challenges/html5-and-css.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/html5-and-css.json b/seed/challenges/html5-and-css.json index 82f2c72603..255b11ad08 100644 --- a/seed/challenges/html5-and-css.json +++ b/seed/challenges/html5-and-css.json @@ -1457,7 +1457,7 @@ "  <li>cheese</li>", "</ul>", "would create a bullet point-style list of \"milk\" and \"cheese\".", - "Replace your p elements with an unordered list of three things that cats love." + "Remove the last two p elements and create an unordered list of three things that cats love at the bottom of the page." ], "tests": [ "assert($(\"ul\").length > 0, 'Create a ul element.')", From 0bba8712f99b09bb71dc2f9e8d1abdc34b654ef8 Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Sun, 1 Nov 2015 11:07:44 -0800 Subject: [PATCH 26/33] Fix Waypoint Center Text With Bootstrap Test Closes #3864 --- seed/challenges/bootstrap.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index 4306f0b5f7..913d4a26db 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -183,7 +183,8 @@ "<h2 class=\"red-text text-center\">your text</h2>" ], "tests": [ - "assert($(\"h2\").hasClass(\"text-center\"), 'Your h2 element should be centered by applying the class text-center')" + "assert($(\"h2\").hasClass(\"text-center\"), 'Your h2 element should be centered by applying the class text-center')", + "assert($(\"h2\").hasClass(\"red-text\"), 'Your h2 element should still have the class red-text')" ], "challengeSeed": [ "", From 9a63bd60b6d9ccbb34b1f58c3a467afce9cff766 Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Sun, 1 Nov 2015 11:22:27 -0800 Subject: [PATCH 27/33] Fix Create Radio Buttons Redundant Wording Closes #3856 --- seed/challenges/html5-and-css.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/html5-and-css.json b/seed/challenges/html5-and-css.json index 82f2c72603..b56fe174d4 100644 --- a/seed/challenges/html5-and-css.json +++ b/seed/challenges/html5-and-css.json @@ -2024,7 +2024,7 @@ "All related radio buttons should have the same name attribute.", "Here's an example of a radio button:", "<label><input type=\"radio\" name=\"indoor-outdoor\"> Indoor</label>", - "Add to your form a pair of radio buttons. Each radio button should be nested within its own label element. They should share a common name attribute. One should have the option of indoor and the other should have the option of outdoor." + "Add a pair of radio buttons to your form. One should have the option of indoor and the other should have the option of outdoor." ], "tests": [ "assert($('input[type=\"radio\"]').length > 1, 'Your page should have two radio button elements.')", From ab31b3c3e26dc6770d69e5ade898ec51a94164b1 Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Sun, 1 Nov 2015 11:39:12 -0800 Subject: [PATCH 28/33] Fix waypoint target html with jquery selectors wording Closes #3842 --- seed/challenges/jquery.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/jquery.json b/seed/challenges/jquery.json index 37a0899018..0026d2bb02 100644 --- a/seed/challenges/jquery.json +++ b/seed/challenges/jquery.json @@ -59,7 +59,7 @@ "jQuery often selects an HTML element with a selector, then does something to that element.", "For example, let's make all of your button elements bounce. Just add this code inside your document ready function:", "$(\"button\").addClass(\"animated bounce\")", - "Note that we've already included both the jQuery library and the Animate.css library in your code editor. So you are using jQuery to apply the Animate.css bounce class to your button elements." + "Note that we've already included both the jQuery library and the Animate.css library in the background so that you can use them in the editor. So you are using jQuery to apply the Animate.css bounce class to your button elements." ], "tests": [ "assert($(\"button\").hasClass(\"animated\") && $(\"button\").hasClass(\"bounce\"), 'Use the jQuery addClass() function to give the classes animated and bounce to your button elements.')", From 4959675d74f0a095af5f99d65c0767f9c3a03352 Mon Sep 17 00:00:00 2001 From: Logan Tegman Date: Sun, 1 Nov 2015 17:11:23 -0800 Subject: [PATCH 29/33] Fix Sidebar Code Indentation Character Closes #3988 --- seed/challenges/basic-javascript.json | 34 ++++++------- seed/challenges/html5-and-css.json | 48 +++++++++---------- seed/challenges/json-apis-and-ajax.json | 30 ++++++------ ...t-oriented-and-functional-programming.json | 14 +++--- 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index a871f43675..6eaf92406d 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -653,7 +653,7 @@ "In JavaScript, we can divide up our code into reusable parts called functions.", "Here's an example of a function:", "function functionName(a, b) {", - "  return a + b;", + "  return a + b;", "}", "After writing the above lines in our code, we can then pass values to our function and the result following the return statement will be returned.", "For example, we can pass numbers 4 and 2 by “calling” the function later in our code like this: functionName(4, 2).", @@ -694,10 +694,10 @@ "Objects are similar to arrays, except that instead of using indexes to access and modify their data, you access the data in objects through what are called properties.", "Here's a sample object:", "var cat = {", - "  \"name\": \"Whiskers\",", - "  \"legs\": 4,", - "  \"tails\": 1,", - "  \"enemies\": [\"Water\", \"Dogs\"]", + "  \"name\": \"Whiskers\",", + "  \"legs\": 4,", + "  \"tails\": 1,", + "  \"enemies\": [\"Water\", \"Dogs\"]", "};", "", "Objects are useful for storing data in a structured way, and can represent real world objects, like a cat.", @@ -741,10 +741,10 @@ "After you've created a JavaScript object, you can update its properties at any time just like you would update any other variable.", "For example, let's look at ourDog:", "var ourDog = {", - "  \"name\": \"Camper\",", - "  \"legs\": 4,", - "  \"tails\": 1,,", - "  \"friends\": [\"everything!\"]", + "  \"name\": \"Camper\",", + "  \"legs\": 4,", + "  \"tails\": 1,,", + "  \"friends\": [\"everything!\"]", "};", "Since he's a particularly happy dog, let's change his name to \"Happy Camper\". Here's how we update his object's name property:", "ourDog.name = \"Happy Camper\";", @@ -874,7 +874,7 @@ "In the following example we initialize with i = 0 and iterate while our condition i < 5 is true. We'll increment i by 1 in each loop iteration with i++ as our final-expression.", "var ourArray = [];", "for(var i = 0; i < 5; i++) {", - "  ourArray.push(i);", + "  ourArray.push(i);", "}", "ourArray will now contain [0,1,2,3,4].", "Let's try getting a for loop to work by pushing values to an array." @@ -912,7 +912,7 @@ "We'll start at i = 0 and loop while i < 10. We'll increment i by 2 each loop with i += 2.", "var ourArray = [];", "for(var i = 0; i < 10; i += 2) {", - "  ourArray.push(i);", + "  ourArray.push(i);", "}", "ourArray will now contain [0,2,4,6,8] ", "Let's change our initialization and final-expression so we can count by odd numbers.", @@ -952,7 +952,7 @@ "We'll start at i = 10 and loop while i > 0. We'll decrement i by 2 each loop with i -= 2.", "var ourArray = [];", "for(var i = 10; i > 0; i -= 2) {", - "  ourArray.push(i);", + "  ourArray.push(i);", "}", "ourArray will now contain [10,8,6,4,2]", "Let's change our initialization and final-expression so we can count backward by twos for numbers.", @@ -992,8 +992,8 @@ "var ourArray = [];", "var i = 0;", "while(i < 5) {", - "  ourArray.push(i);", - "  i++;", + "  ourArray.push(i);", + "  i++;", "}", "Let's try getting a while loop to work by pushing values to an array.", "Push the numbers 0 through 4 to myArray using a while loop." @@ -1140,9 +1140,9 @@ "if statements require some sort of boolean condition to evaluate.", "For example:", "if (1 === 2) {", - "  return true;", + "  return true;", "} else {", - "  return false;", + "  return false;", "}", "Let's use if and else statements to make a coin-flip game.", "Create if and else statements to return the string \"heads\" if the flip variable is zero, or else return the string \"tails\" if the flip variable is not zero." @@ -1460,7 +1460,7 @@ "If all three numbers match, we should return the number that we have in three of slots or leave it as null.", "Let's create an if statement with multiple conditions in order to check whether all numbers are equal.", "if(slotOne !== slotTwo || slotTwo !== slotThree){", - "  return null;", + "  return null;", "}" ], "tests": [ diff --git a/seed/challenges/html5-and-css.json b/seed/challenges/html5-and-css.json index 82f2c72603..dc5fd59eed 100644 --- a/seed/challenges/html5-and-css.json +++ b/seed/challenges/html5-and-css.json @@ -381,7 +381,7 @@ "</style>", "Inside that style element, you can create a CSS selector for all h2 elements. For example, if you wanted all h2 elements to be red, your style element would look like this:", "<style>", - "  h2 {color: red;}", + "  h2 {color: red;}", "</style>", "Note that it's important to have both opening and closing curly braces ({ and }) around each element's style. You also need to make sure your element's style is between the opening and closing style tags. Finally, be sure to add the semicolon to the end of each of your element's styles.", "Delete your h2 element's style attribute and instead create a CSS style element. Add the necessary CSS to turn all h2 elements blue." @@ -436,9 +436,9 @@ "Classes are reusable styles that can be added to HTML elements.", "Here's an example CSS class declaration:", "<style>", - "  .blue-text {", - "    color: blue;", - "  }", + "  .blue-text {", + "    color: blue;", + "  }", "</style>", "You can see that we've created a CSS class called blue-text within the <style> tag.", "You can apply a class to an HTML element like this:", @@ -504,7 +504,7 @@ "Remember that you can attach classes to HTML elements by using class=\"your-class-here\" within the relevant element's opening tag.", "Remember that CSS class selectors require a period at the beginning like this:", ".blue-text {", - "  color: blue;", + "  color: blue;", "}", "But also remember that class declarations don't use a period, like this:", "<h2 class=\"blue-text\">CatPhotoApp<h2>", @@ -556,7 +556,7 @@ "description": [ "Font size is controlled by the font-size CSS property, like this:", "h1 {", - "  font-size: 30px;", + "  font-size: 30px;", "}", "See if you can figure out how to give both of your p elements the font-size of 16 pixels (16px). You can do this inside the same <style> tag that we created for your red-text class.", "Create a second p element with the following kitty ipsum text: Purr jump eat the grass rip the couch scratched sunbathe, shed everywhere rip the couch sleep in the sink fluffy fur catnip scratched.", @@ -611,7 +611,7 @@ "You can set an element's font by using the font-family property.", "For example, if you wanted to set your h2 element's font to Sans-serif, you would use the following CSS:", "h2 {", - "  font-family: Sans-serif;", + "  font-family: Sans-serif;", "}", "Make all of your p elements use the Monospace font." ], @@ -715,7 +715,7 @@ "When one font isn't available, you can tell the browser to \"degrade\" to another font.", "For example, if you wanted an element to use the Helvetica font, but also degrade to the Sans-Serif font when Helvetica wasn't available, you could use this CSS style:", "p {", - "  font-family: Helvetica, Sans-Serif;", + "  font-family: Helvetica, Sans-Serif;", "}", "Now comment out your call to Google Fonts, so that the Lobster font isn't available. Notice how it degrades to the Monospace font." ], @@ -832,9 +832,9 @@ "CSS has a property called width that controls an element's width. Just like with fonts, we'll use px (pixels) to specify the image's width.", "For example, if we wanted to create a CSS class called larger-image that gave HTML elements a width of 500 pixels, we'd use:", "<style>", - "  .larger-image {", - "    width: 500px;", - "  }", + "  .larger-image {", + "    width: 500px;", + "  }", "</style>", "Create a class called smaller-image and use it to resize the image so that it's only 100 pixels wide." ], @@ -892,11 +892,11 @@ "CSS borders have properties like style, color and width", "For example, if we wanted to create a red, 5 pixel border around an HTML element, we could use this class:", "<style>", - "  .thin-red-border {", - "    border-color: red;", - "    border-width: 5px;", - "    border-style: solid;", - "  }", + "  .thin-red-border {", + "    border-color: red;", + "    border-width: 5px;", + "    border-style: solid;", + "  }", "</style>", "Create a class called thick-green-border that puts a 10-pixel-wide green border with a style of solid around an HTML element, and apply that class to your cat photo.", "Remember that you can apply multiple classes to an element by separating each class with a space within its class attribute. For example:", @@ -1453,8 +1453,8 @@ "Unordered lists start with a <ul> element. Then they contain some number of <li> elements.", "For example: ", "<ul>", - "  <li>milk</li>", - "  <li>cheese</li>", + "  <li>milk</li>", + "  <li>cheese</li>", "</ul>", "would create a bullet point-style list of \"milk\" and \"cheese\".", "Replace your p elements with an unordered list of three things that cats love." @@ -1530,8 +1530,8 @@ "Ordered lists start with a <ol> element. Then they contain some number of <li> elements.", "For example:", "<ol>", - "  <li>Garfield</li>", - "  <li>Sylvester</li>", + "  <li>Garfield</li>", + "  <li>Sylvester</li>", "</ol>", "would create a numbered list of \"Garfield\" and \"Sylvester\".", "Create an ordered list of the top 3 things cats hate the most." @@ -2386,7 +2386,7 @@ "You can set an element's background color with the background-color property.", "For example, if you wanted an element's background color to be green, you'd put this within your style element:", ".green-background {", - "  background-color: green;", + "  background-color: green;", "}", "Create a class called gray-background with the background-color of gray. Assign this class to your div element." ], @@ -2566,7 +2566,7 @@ "One cool thing about id attributes is that, like classes, you can style them using CSS.", "Here's an example of how you can take your element with the id attribute of cat-photo-element and give it the background color of green. In your style element:", "#cat-photo-element {", - "  background-color: green;", + "  background-color: green;", "}", "Note that inside your style element, you always reference classes by putting a . in front of their names. You always reference ids by putting a # in front of their names.", "Try giving your form, which now has the id attribute of cat-photo-form, a green background." @@ -3153,7 +3153,7 @@ "We can prove that the body element exists here by giving it a background-color of black.", "We can do this by adding the following to our style element:", "body {", - "  background-color: black;", + "  background-color: black;", "}" ], "tests": [ @@ -3316,7 +3316,7 @@ "Leave the blue-text and pink-text classes on your h1 element.", "Create a CSS declaration for your orange-text id in your style element. Here's an example of what this looks like:", "#brown-text {", - "  color: brown;", + "  color: brown;", "}" ], "tests": [ diff --git a/seed/challenges/json-apis-and-ajax.json b/seed/challenges/json-apis-and-ajax.json index 6c44cabac4..5d3b0a5e6f 100644 --- a/seed/challenges/json-apis-and-ajax.json +++ b/seed/challenges/json-apis-and-ajax.json @@ -58,7 +58,7 @@ "When our click event happens, we can use Ajax to update an HTML element.", "Let's make it so that when a user clicks the \"Get Message\" button, we change the text of the element with the class message to say \"Here is the message\".", "We can do this by adding the following code within our click event:", - "  $(\".message\").html(\"Here is the message\");" + "  $(\".message\").html(\"Here is the message\");" ], "tests": [ "assert(editor.match(/\\$\\s*?\\(\\s*?(?:'|\")\\.message(?:'|\")\\s*?\\)\\s*?\\.html\\s*?\\(\\s*?(?:'|\")Here\\sis\\sthe\\smessage(?:'|\")\\s*?\\);/gi), 'Clicking the \"Get Message\" button should give the element with the class message the text \"Here is the message\".')" @@ -107,9 +107,9 @@ "These properties and their values are often referred to as \"key-value pairs\".", "Let's get the JSON from Free Code Camp's Cat Photo API.", "Here's the code you can put in your click event to do this:", - "  $.getJSON(\"/json/cats.json\", function(json) {", - "    $(\".message\").html(JSON.stringify(json));", - "  });", + "  $.getJSON(\"/json/cats.json\", function(json) {", + "    $(\".message\").html(JSON.stringify(json));", + "  });", "Once you've added this, click the \"Get Message\" button. Your Ajax function will replace the \"The message will go here\" text with the raw JSON output from the Free Code Camp Cat Photo API." ], "tests": [ @@ -165,12 +165,12 @@ "Then, let's loop through our JSON, adding more HTML to that variable. When the loop is finished, we'll render it.", "Here's the code that does this:", "json.map(function(val) {", - "  var keys = Object.keys(val);", - "  html += \"<div class = 'cat'>\";", - "  keys.map(function(key) {", - "    html += \"<b>\" + key + \"</b>: \" + val[key] + \"<br>\";", - "  });", - "  html += \"</div><br>\";", + "  var keys = Object.keys(val);", + "  html += \"<div class = 'cat'>\";", + "  keys.map(function(key) {", + "    html += \"<b>\" + key + \"</b>: \" + val[key] + \"<br>\";", + "  });", + "  html += \"</div><br>\";", "});" ], "tests": [ @@ -225,7 +225,7 @@ "We've seen from the last two lessons that each object in our JSON array contains an imageLink key with a value that is the url of a cat's image.", "When we're looping through these objects, let's use this imageLink property to display this image in an img element.", "Here's the code that does this:", - "  html += \"<img src = '\" + val.imageLink + \"'>\";" + "  html += \"<img src = '\" + val.imageLink + \"'>\";" ], "tests": [ "assert(editor.match(/val.imageLink/gi), 'You should have used the imageLink property to display the images.')" @@ -289,7 +289,7 @@ "Let's filter out the cat who's \"id\" key has a value of 1.", "Here's the code to do this:", "json = json.filter(function(val) {", - "  return(val.id !== 1);", + "  return(val.id !== 1);", "});" ], "tests": [ @@ -358,9 +358,9 @@ "By selecting allow you will see the text on the output phone change to your latitude and longitude", "Here's some code that does this:", "if (navigator.geolocation) {", - "  navigator.geolocation.getCurrentPosition(function(position) {", - "    $(\"#data\").html(\"latitude: \" + position.coords.latitude + \"<br>longitude: \" + position.coords.longitude);", - "  });", + "  navigator.geolocation.getCurrentPosition(function(position) {", + "    $(\"#data\").html(\"latitude: \" + position.coords.latitude + \"<br>longitude: \" + position.coords.longitude);", + "  });", "}" ], "tests": [ diff --git a/seed/challenges/object-oriented-and-functional-programming.json b/seed/challenges/object-oriented-and-functional-programming.json index 3be9cb4a20..810fc81323 100644 --- a/seed/challenges/object-oriented-and-functional-programming.json +++ b/seed/challenges/object-oriented-and-functional-programming.json @@ -54,9 +54,9 @@ "We are also able to create objects using constructor functions.", "Here's an example of a constructor function:", "var Car = function() {", - "  this.wheels = 4;", - "  this.engines = 1;", - "  this.seats = 1;", + "  this.wheels = 4;", + "  this.engines = 1;", + "  this.seats = 1;", "};", "Give your myMotorBike object a wheels, engines and seats attribute and set them to numbers.", "You may be confused by the this keyword here. Don't worry, we will get to that very soon." @@ -190,7 +190,7 @@ "description":[ "The map method is a convenient way to iterate through arrays. Here's an example usage:", "var timesFour = oldArray.map(function(val){", - "  return val * 4;", + "  return val * 4;", "});", "", "The map method will iterate through every element of the array, creating a new array with values that have been modified by the callback function, and return it.", @@ -228,7 +228,7 @@ "reduce has an optional second argument which can be used to set the initial value of the accumulator. If no initial value is specified it will be the first array element and currentVal will start with the second array element.", "Here is an example of reduce being used to subtract all the values of an array:", "var singleVal = array.reduce(function(previousVal, currentVal) {", - "  return previousVal - currentVal;", + "  return previousVal - currentVal;", "}, 0);", "Use the reduce method to sum all the values in array and assign it to singleVal." ], @@ -263,7 +263,7 @@ "The following code is an example of using filter to remove array elements that are not even numbers:", "Note: We omit the second and third arguments since we only need the value", "array = array.filter(function(val) {", - "  return val % 2 === 0;", + "  return val % 2 === 0;", "});", "Use filter to remove all elements from array that are greater than 5." ], @@ -296,7 +296,7 @@ "Here is an example of using sort with a compare function that will sort the elements from smallest to largest number:", "var array = [1, 12, 21, 2];", "array.sort(function(a, b) {", - "  return a - b;", + "  return a - b;", "});", "Use sort to sort array from largest to smallest." ], From 921080f3d2ca71985060a2273d72257c0a35c6e4 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sun, 1 Nov 2015 17:20:03 -0800 Subject: [PATCH 30/33] Add challenge testing --- .travis.yml | 3 +- package.json | 9 +- seed/challenges/advanced-bonfires.json | 1 - seed/challenges/angularjs.json | 35 ++++++- seed/challenges/basic-bonfires.json | 8 +- seed/challenges/intermediate-bonfires.json | 43 ++++---- seed/getChallenges.js | 19 ++++ seed/index.js | 16 +-- seed/test-challenges.js | 108 +++++++++++++++++++++ 9 files changed, 194 insertions(+), 48 deletions(-) create mode 100644 seed/getChallenges.js create mode 100644 seed/test-challenges.js diff --git a/.travis.yml b/.travis.yml index b6bd11e2c2..22cfb84161 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - - 'node' - - '1.6.4' + - '4.2.1' sudo: false diff --git a/package.json b/package.json index 1c6f77559a..7879ae7d5b 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,11 @@ "prestart-production": "bower cache clean && bower install && gulp build", "start-production": "node pm2Start", "lint": "eslint --ext=.js,.jsx .", - "test": "gulp test-challenges" + "lint-challenges": "jsonlint -q seed/challenges/*.json", + "lint-nonprofits": "jsonlint -q seed/nonprofits.json", + "test-challenges": "babel-node seed/test-challenges.js | tnyan", + "pretest": "npm run lint-challenges && npm run lint-nonprofits", + "test": "npm run test-challenges" }, "license": "(BSD-3-Clause AND CC-BY-SA-4.0)", "dependencies": { @@ -124,11 +128,14 @@ "chai": "~1.10.0", "envify": "^3.4.0", "istanbul": "^0.3.15", + "jsonlint": "^1.6.2", "loopback-explorer": "^1.7.2", "loopback-testing": "^1.1.0", "mocha": "~2.0.1", "multiline": "~1.0.1", "supertest": "~0.15.0", + "tap-nyan": "0.0.2", + "tape": "^4.2.2", "vinyl-source-stream": "^1.1.0" } } diff --git a/seed/challenges/advanced-bonfires.json b/seed/challenges/advanced-bonfires.json index 04ab7c18b2..115b236eba 100644 --- a/seed/challenges/advanced-bonfires.json +++ b/seed/challenges/advanced-bonfires.json @@ -157,7 +157,6 @@ "Global Object" ], "solutions": [ - "var VALUES = [1, 5, 10, 25, 100, 500, 1000, 2000, 10000];\n\nfunction drawer(price, cash, cid) {\n cash = ~~(cash * 100);\n price = ~~(price * 100);\n var diff = cash-price;\n cid.forEach(function(c) {\n c[1] = ~~(c[1] * 100);\n });\n var totalCid = cid.reduce(function(a, c) {\n return a + c[1];\n }, 0);\n if (diff > totalCid) {\n return \"Insufficient Funds\";\n }\n if (diff === totalCid) {\n return \"Closed\";\n }\n \n var change = []; \n var index = cid.length;\n while (diff > 0 && --index > -1) {\n var t = 0;\n var value = VALUES[index];\n while (diff >= value && cid[index][1] > 0) {\n t += value;\n cid[index][1] -= value;\n diff -= value;\n }\n if (t) {\n change.push([cid[index][0], t/100]);\n }\n console.log(JSON.stringify(change));\n }\n // Here is your change, ma'am.\n return change;\n}\n\n// Example cash-in-drawer array:\n// [['PENNY', 1.01],\n// ['NICKEL', 2.05],\n// ['DIME', 3.10],\n// ['QUARTER', 4.25],\n// ['ONE', 90.00],\n// ['FIVE', 55.00],\n// ['TEN', 20.00],\n// ['TWENTY', 60.00],\n// ['ONE HUNDRED', 100.00]]\n\ndrawer(19.50, 20.00, [['PENNY', 1.01], ['NICKEL', 2.05], ['DIME', 3.10], ['QUARTER', 4.25], ['ONE', 90.00], ['FIVE', 55.00], ['TEN', 20.00], ['TWENTY', 60.00], ['ONE HUNDRED', 100.00]]);\n" ], "type": "bonfire", "challengeType": 5, diff --git a/seed/challenges/angularjs.json b/seed/challenges/angularjs.json index 905c120f33..c55221e0fe 100644 --- a/seed/challenges/angularjs.json +++ b/seed/challenges/angularjs.json @@ -14,7 +14,12 @@ ], "type": "waypoint", "challengeType": 2, - "tests": [], + "tests": [ + "assert(true, 'No test needed');" + ], + "solutions": [ + "/* no test needed */" + ], "nameCn": "", "descriptionCn": [], "nameFr": "", @@ -37,7 +42,12 @@ ], "type": "waypoint", "challengeType": 2, - "tests": [], + "tests": [ + "assert(true, 'No test needed');" + ], + "solutions": [ + "/* no test needed */" + ], "nameCn": "", "descriptionCn": [], "nameFr": "", @@ -60,7 +70,12 @@ ], "type": "waypoint", "challengeType": 2, - "tests": [], + "tests": [ + "assert(true, 'No test needed');" + ], + "solutions": [ + "/* no test needed */" + ], "nameCn": "", "descriptionCn": [], "nameFr": "", @@ -82,7 +97,12 @@ ], "type": "waypoint", "challengeType": 2, - "tests": [], + "tests": [ + "assert(true, 'No test needed');" + ], + "solutions": [ + "/* no test needed */" + ], "nameCn": "", "descriptionCn": [], "nameFr": "", @@ -105,7 +125,12 @@ ], "type": "waypoint", "challengeType": 2, - "tests": [], + "tests": [ + "assert(true, 'No test needed');" + ], + "solutions": [ + "/* no test needed */" + ], "nameCn": "", "descriptionCn": [], "nameFr": "", diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json index 8cef5e19ca..505b91fd5e 100644 --- a/seed/challenges/basic-bonfires.json +++ b/seed/challenges/basic-bonfires.json @@ -116,7 +116,7 @@ "Arithmetic Operators" ], "solutions": [ - "function factorialize(num) {\n return num === 1 ? 1 : num * factorialize(num-1);\n}\n\nfactorialize(5);\n" + "function factorialize(num) {\n return num < 1 ? 1 : num * factorialize(num-1);\n}\n\nfactorialize(5);\n" ], "type": "bonfire", "challengeType": 5, @@ -169,7 +169,6 @@ "String.toLowerCase()" ], "solutions": [ - "function palindrome(str) {\n var a = str.toLowerCase().replace(/[^a-z]/g, '');\n console.log(a.split('').reverse().join(''));\n return a == a.split('').reverse().join('');\n}\n\n\n\npalindrome(\"eye\");\npalindrome(\"A man, a plan, a canal. Panama\");\n" ], "type": "bonfire", "challengeType": 5, @@ -417,7 +416,6 @@ "String.slice()" ], "solutions": [ - "function truncate(str, num) {\n if (str.length > num) {\n return str.substring(0, num-3) + '...';\n }\n return str;\n}\n\ntruncate('A-tisket a-tasket A green and yellow basket', 11);\n" ], "type": "bonfire", "challengeType": 5, @@ -663,9 +661,7 @@ "MDNlinks": [ "Array.sort()" ], - "solutions": [ - "function where(arr, num) {\n // Find my place in this sorted array.\n return num;\n}\n\nwhere([40, 60], 50);\n" - ], + "solutions": [], "tests": [ "assert(where([10, 20, 30, 40, 50], 35) === 3, 'message: where([10, 20, 30, 40, 50], 35) should return 3.');", "assert(where([10, 20, 30, 40, 50], 30) === 2, 'message: where([10, 20, 30, 40, 50], 30) should return 2.');", diff --git a/seed/challenges/intermediate-bonfires.json b/seed/challenges/intermediate-bonfires.json index 9ae783a1a5..6d90d9fdd7 100644 --- a/seed/challenges/intermediate-bonfires.json +++ b/seed/challenges/intermediate-bonfires.json @@ -31,7 +31,7 @@ "Array.reduce()" ], "solutions": [ - "function sumAll(arr) {\n var sum = 0;\n arr.sort(function(a,b) {return a-b;});\n for (var i = arr[0]; i <= arr[1]; i++) {\n sum += i; \n }\n return sum;\n}\n\nsumAll([1, 4]);\n" + "function sumAll(arr) {\n var sum = 0;\n arr.sort(function(a,b) {return a-b;});\n for (var i = arr[0]; i <= arr[1]; i++) {\n sum += i; \n }\n return sum;\n}" ], "type": "bonfire", "challengeType": 5, @@ -80,7 +80,7 @@ "Array.concat()" ], "solutions": [ - "function diff(arr1, arr2) {\n var newArr = [];\n var h1 = Object.create(null);\n arr1.forEach(function(e) {\n h1[e] = e;\n });\n \n var h2 = Object.create(null);\n arr2.forEach(function(e) {\n h2[e] = e;\n });\n \n Object.keys(h1).forEach(function(e) {\n if (!(e in h2)) newArr.push(h1[e]);\n });\n Object.keys(h2).forEach(function(e) {\n if (!(e in h1)) newArr.push(h2[e]);\n });\n // Same, same; but different.\n return newArr;\n}\n\ndiff([1, 2, 3, 5], [1, 2, 3, 4, 5]);\n" + "function diff(arr1, arr2) {\n var newArr = [];\n var h1 = Object.create(null);\n arr1.forEach(function(e) {\n h1[e] = e;\n });\n \n var h2 = Object.create(null);\n arr2.forEach(function(e) {\n h2[e] = e;\n });\n \n Object.keys(h1).forEach(function(e) {\n if (!(e in h2)) newArr.push(h1[e]);\n });\n Object.keys(h2).forEach(function(e) {\n if (!(e in h1)) newArr.push(h2[e]);\n });\n // Same, same; but different.\n return newArr;\n}" ], "type": "bonfire", "challengeType": 5, @@ -138,7 +138,7 @@ "Array.join()" ], "solutions": [ - "function convert(num) {\n var ref = [['M', 1000], ['CM', 900], ['D', 500], ['CD', 400], ['C', 100], ['XC', 90], ['L', 50], ['XL', 40], ['X', 10], ['IX', 9], ['V', 5], ['IV', 4], ['I', 1]];\n var res = [];\n ref.forEach(function(p) {\n while (num >= p[1]) {\n res.push(p[0]);\n num -= p[1];\n }\n });\n return res.join('');\n}\n\nconvert(36);\n" + "function convert(num) {\n var ref = [['M', 1000], ['CM', 900], ['D', 500], ['CD', 400], ['C', 100], ['XC', 90], ['L', 50], ['XL', 40], ['X', 10], ['IX', 9], ['V', 5], ['IV', 4], ['I', 1]];\n var res = [];\n ref.forEach(function(p) {\n while (num >= p[1]) {\n res.push(p[0]);\n num -= p[1];\n }\n });\n return res.join('');\n}" ], "type": "bonfire", "challengeType": 5, @@ -181,7 +181,7 @@ "Object.keys()" ], "solutions": [ - "function where(collection, source) {\n var arr = [];\n var keys = Object.keys(source);\n collection.forEach(function(e) {\n if(keys.every(function(key) {return e[key] === source[key];})) {\n arr.push(e); \n }\n });\n return arr;\n}\n\nwhere([{ first: 'Romeo', last: 'Montague' }, { first: 'Mercutio', last: null }, { first: 'Tybalt', last: 'Capulet' }], { last: 'Capulet' });\n" + "function where(collection, source) {\n var arr = [];\n var keys = Object.keys(source);\n collection.forEach(function(e) {\n if(keys.every(function(key) {return e[key] === source[key];})) {\n arr.push(e); \n }\n });\n return arr;\n}" ], "type": "bonfire", "challengeType": 5, @@ -227,7 +227,7 @@ "Array.join()" ], "solutions": [ - "function replace(str, before, after) {\n if (before.charAt(0) === before.charAt(0).toUpperCase()) {\n after = after.charAt(0).toUpperCase() + after.substring(1);\n } else {\n after = after.charAt(0).toLowerCase() + after.substring(1);\n }\n return str.replace(before, after);\n}\n\nreplace(\"A quick brown fox jumped over the lazy dog\", \"jumped\", \"leaped\");\n" + "function myReplace(str, before, after) {\n if (before.charAt(0) === before.charAt(0).toUpperCase()) {\n after = after.charAt(0).toUpperCase() + after.substring(1);\n } else {\n after = after.charAt(0).toLowerCase() + after.substring(1);\n }\n return str.replace(before, after);\n}" ], "type": "bonfire", "challengeType": 5, @@ -273,7 +273,7 @@ "String.split()" ], "solutions": [ - "function translate(str) {\n if (isVowel(str.charAt(0))) return str + \"way\";\n var front = [];\n str = str.split('');\n while (str.length && !isVowel(str[0])) {\n front.push(str.shift());\n }\n return [].concat(str, front).join('') + 'ay';\n}\n\nfunction isVowel(c) {\n return ['a', 'e', 'i', 'o', 'u'].indexOf(c.toLowerCase()) !== -1;\n}\n\ntranslate(\"consonant\");\n" + "function translate(str) {\n if (isVowel(str.charAt(0))) return str + \"way\";\n var front = [];\n str = str.split('');\n while (str.length && !isVowel(str[0])) {\n front.push(str.shift());\n }\n return [].concat(str, front).join('') + 'ay';\n}\n\nfunction isVowel(c) {\n return ['a', 'e', 'i', 'o', 'u'].indexOf(c.toLowerCase()) !== -1;\n}" ], "type": "bonfire", "challengeType": 5, @@ -316,7 +316,7 @@ "String.split()" ], "solutions": [ - "var lookup = Object.create(null);\nlookup.A = 'T';\nlookup.T = 'A';\nlookup.C = 'G';\nlookup.G = 'C';\n\nfunction pair(str) {\n return str.split('').map(function(p) {return [p, lookup[p]];});\n}\n\npair(\"GCG\");\n" + "var lookup = Object.create(null);\nlookup.A = 'T';\nlookup.T = 'A';\nlookup.C = 'G';\nlookup.G = 'C';\n\nfunction pair(str) {\n return str.split('').map(function(p) {return [p, lookup[p]];});\n}" ], "type": "bonfire", "challengeType": 5, @@ -357,7 +357,7 @@ "String.fromCharCode()" ], "solutions": [ - "function fearNotLetter(str) {\n var s = str.split('').map(function(c) {return c.charCodeAt(0);});\n for (var i = 1; i < s.length; i++) {\n if (s[i]-1 != s[i-1]) {\n return String.fromCharCode(s[i]-1);\n }\n }\n}\n\nfearNotLetter('abce');\n" + "function fearNotLetter(str) {\n var s = str.split('').map(function(c) {return c.charCodeAt(0);});\n for (var i = 1; i < s.length; i++) {\n if (s[i]-1 != s[i-1]) {\n return String.fromCharCode(s[i]-1);\n }\n }\n}" ], "type": "bonfire", "challengeType": 5, @@ -402,7 +402,7 @@ "Boolean Objects" ], "solutions": [ - "function boo(bool) {\n // What is the new fad diet for ghost developers? The Boolean.\n return typeof(bool) === \"boolean\";\n}\n\nboo(null);\n" + "function boo(bool) {\n // What is the new fad diet for ghost developers? The Boolean.\n return typeof(bool) === \"boolean\";\n}\n\nboo(null);" ], "type": "bonfire", "challengeType": 5, @@ -445,7 +445,7 @@ "Array.reduce()" ], "solutions": [ - "function unite(arr1, arr2, arr3) {\n return [].slice.call(arguments).reduce(function(a, b) {\n return [].concat(a, b.filter(function(e) {return a.indexOf(e) === -1;}));\n }, []);\n}\n\nunite([1, 2, 3], [5, 2, 1, 4], [2, 1]);\n" + "function unite(arr1, arr2, arr3) {\n return [].slice.call(arguments).reduce(function(a, b) {\n return [].concat(a, b.filter(function(e) {return a.indexOf(e) === -1;}));\n }, []);\n}" ], "type": "bonfire", "challengeType": 5, @@ -489,7 +489,7 @@ "HTML Entities" ], "solutions": [ - "var MAP = { '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''};\n\nfunction convert(str) {\n return str.replace(/[&<>\"']/g, function(c) {\n return MAP[c];\n });\n}\n\nconvert('Dolce & Gabbana');\n" + "var MAP = { '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''};\n\nfunction convert(str) {\n return str.replace(/[&<>\"']/g, function(c) {\n return MAP[c];\n });\n}" ], "type": "bonfire", "challengeType": 5, @@ -531,7 +531,7 @@ "String.replace()" ], "solutions": [ - "function spinalCase(str) {\n // \"It's such a fine line between stupid, and clever.\"\n // --David St. Hubbins\n str = str.replace(/([a-z](?=[A-Z]))/g, '$1 ');\n return str.toLowerCase().replace(/\\ |\\_/g, '-');\n}\n\nspinalCase('This Is Spinal Tap');\n" + "function spinalCase(str) {\n // \"It's such a fine line between stupid, and clever.\"\n // --David St. Hubbins\n str = str.replace(/([a-z](?=[A-Z]))/g, '$1 ');\n return str.toLowerCase().replace(/\\ |\\_/g, '-');\n}" ], "type": "bonfire", "challengeType": 5, @@ -574,7 +574,7 @@ "Remainder" ], "solutions": [ - "function sumFibs(num) {\n var a = 1; \n var b = 1;\n var s = 0;\n while (a <= num) {\n if (a % 2 !== 0) { \n s += a; \n }\n a = [b, b=b+a][0];\n }\n return s;\n}\n\nsumFibs(4);\n" + "function sumFibs(num) {\n var a = 1; \n var b = 1;\n var s = 0;\n while (a <= num) {\n if (a % 2 !== 0) { \n s += a; \n }\n a = [b, b=b+a][0];\n }\n return s;\n}" ], "type": "bonfire", "challengeType": 5, @@ -615,7 +615,7 @@ "Array.push()" ], "solutions": [ - "function eratosthenesArray(n) {\n var primes = [];\n if (n > 2) {\n var half = n>>1;\n var sieve = Array(half);\n for (var i = 1, limit = Math.sqrt(n)>>1; i <= limit; i++) {\n if (!sieve[i]) {\n for (var step = 2*i+1, j = (step*step)>>1; j < half; j+=step) {\n sieve[j] = true;\n }\n }\n }\n primes.push(2);\n for (var p = 1; p < half; p++) {\n if (!sieve[p]) primes.push(2*p+1);\n }\n }\n return primes;\n}\n\nfunction sumPrimes(num) {\n return eratosthenesArray(num+1).reduce(function(a,b) {return a+b;}, 0);\n}\n\nsumPrimes(10);\n" + "function eratosthenesArray(n) {\n var primes = [];\n if (n > 2) {\n var half = n>>1;\n var sieve = Array(half);\n for (var i = 1, limit = Math.sqrt(n)>>1; i <= limit; i++) {\n if (!sieve[i]) {\n for (var step = 2*i+1, j = (step*step)>>1; j < half; j+=step) {\n sieve[j] = true;\n }\n }\n }\n primes.push(2);\n for (var p = 1; p < half; p++) {\n if (!sieve[p]) primes.push(2*p+1);\n }\n }\n return primes;\n}\n\nfunction sumPrimes(num) {\n return eratosthenesArray(num+1).reduce(function(a,b) {return a+b;}, 0);\n}\n\nsumPrimes(10);" ], "type": "bonfire", "challengeType": 5, @@ -657,7 +657,7 @@ "Smallest Common Multiple" ], "solutions": [ - "function gcd(a, b) {\n while (b !== 0) {\n a = [b, b = a % b][0];\n }\n return a;\n}\n\nfunction lcm(a, b) {\n return (a * b) / gcd(a, b);\n}\n\nfunction smallestCommons(arr) {\n arr.sort(function(a,b) {return a-b;});\n var rng = [];\n for (var i = arr[0]; i <= arr[1]; i++) {\n rng.push(i);\n }\n return rng.reduce(lcm);\n}\n\n\nsmallestCommons([1,5]);\n" + "function gcd(a, b) {\n while (b !== 0) {\n a = [b, b = a % b][0];\n }\n return a;\n}\n\nfunction lcm(a, b) {\n return (a * b) / gcd(a, b);\n}\n\nfunction smallestCommons(arr) {\n arr.sort(function(a,b) {return a-b;});\n var rng = [];\n for (var i = arr[0]; i <= arr[1]; i++) {\n rng.push(i);\n }\n return rng.reduce(lcm);\n}" ], "type": "bonfire", "challengeType": 5, @@ -695,7 +695,7 @@ "Array.filter()" ], "solutions": [ - "function find(arr, func) {\n var num;\n arr.some(function(e) {\n if (func(e)) {\n num = e;\n return true;\n }\n });\n return num;\n}\n\nfind([1, 2, 3, 4], function(num){ return num % 2 === 0; });\n" + "function find(arr, func) {\n var num;\n arr.some(function(e) {\n if (func(e)) {\n num = e;\n return true;\n }\n });\n return num;\n}" ], "type": "bonfire", "challengeType": 5, @@ -736,7 +736,7 @@ "Array.shift()" ], "solutions": [ - "(function drop(arr, func) {\n // Drop them elements.\n while (arr.length && !func(arr[0])) {\n arr.shift();\n }\n return arr;\n}\n\ndrop([1, 2, 3], function(n) {return n < 3; });\n)" + "function drop(arr, func) {\n // Drop them elements.\n while (arr.length && !func(arr[0])) {\n arr.shift();\n }\n return arr;\n}" ], "type": "bonfire", "challengeType": 5, @@ -776,7 +776,7 @@ "Array.isArray()" ], "solutions": [ - "function steamroller(arr) {\n if (!Array.isArray(arr)) {\n return [arr];\n }\n var out = [];\n arr.forEach(function(e) {\n steamroller(e).forEach(function(v) {\n out.push(v);\n });\n });\n return out;\n}\n\nsteamroller([1, [2], [3, [[4]]]]);\n" + "function steamroller(arr) {\n if (!Array.isArray(arr)) {\n return [arr];\n }\n var out = [];\n arr.forEach(function(e) {\n steamroller(e).forEach(function(v) {\n out.push(v);\n });\n });\n return out;\n}" ], "type": "bonfire", "challengeType": 5, @@ -815,7 +815,7 @@ "String.fromCharCode()" ], "solutions": [ - "function binaryAgent(str) {\n return str.split(' ').map(function(s) { return parseInt(s, 2); }).map(function(b) { return String.fromCharCode(b);}).join('');\n}\n\nbinaryAgent('01000001 01110010 01100101 01101110 00100111 01110100 00100000 01100010 01101111 01101110 01100110 01101001 01110010 01100101 01110011 00100000 01100110 01110101 01101110 00100001 00111111');\n" + "function binaryAgent(str) {\n return str.split(' ').map(function(s) { return parseInt(s, 2); }).map(function(b) { return String.fromCharCode(b);}).join('');\n}" ], "type": "bonfire", "challengeType": 5, @@ -858,7 +858,7 @@ "assert.strictEqual(every([{\"single\": \"double\"}, {\"single\": NaN}], \"single\"), false, 'message: every([{\"single\": \"double\"}, {\"single\": NaN}], \"single\") should return false');" ], "solutions": [ - "function every(collection, pre) {\n // Does everyone have one of these?\n return collection.every(function(e) { return e[pre]; });\n}\n\nevery([{'user': 'Tinky-Winky', 'sex': 'male'}, {'user': 'Dipsy', 'sex': 'male'}, {'user': 'Laa-Laa', 'sex': 'female'}, {'user': 'Po', 'sex': 'female'}], 'sex');\n" + "function every(collection, pre) {\n // Does everyone have one of these?\n return collection.every(function(e) { return e[pre]; });\n}" ], "type": "bonfire", "challengeType": 5, @@ -904,8 +904,7 @@ "Arguments object" ], "solutions": [ - "function add() {\n if (arguments.length == 1) {\n var a = arguments[0];\n if (!isNumber(a)) return;\n return function(b) {\n if (!isNumber(b)) return;\n return a+b;\n };\n }\n if (![].slice.call(arguments).every(isNumber)) return;\n return arguments[0] + arguments[1];\n}\n \nfunction isNumber(obj) {\n return toString.call(obj) == '[object Number]';\n}\n\nadd(2,3);\n", - "function add() {\n var a = arguments[0];\n if (toString.call(a) !== '[object Number]') return; \n if (arguments.length === 1) {\n return function(b) {\n if (toString.call(b) !== '[object Number]') return;\n return a + b;\n };\n }\n var b = arguments[1];\n if (toString.call(b) !== '[object Number]') return; \n return a + arguments[1];\n}\n\nadd(2,3);\n" + "function add() {\n var a = arguments[0];\n if (toString.call(a) !== '[object Number]') return; \n if (arguments.length === 1) {\n return function(b) {\n if (toString.call(b) !== '[object Number]') return;\n return a + b;\n };\n }\n var b = arguments[1];\n if (toString.call(b) !== '[object Number]') return; \n return a + arguments[1];\n}" ], "type": "bonfire", "challengeType": 5, diff --git a/seed/getChallenges.js b/seed/getChallenges.js new file mode 100644 index 0000000000..f86a3fc8c6 --- /dev/null +++ b/seed/getChallenges.js @@ -0,0 +1,19 @@ +var fs = require('fs'); +var path = require('path'); + + +function getFilesFor(dir) { + return fs.readdirSync(path.join(__dirname, '/' + dir)); +} + +module.exports = function getChallenges() { + try { + return getFilesFor('challenges') + .map(function(file) { + return require('./challenges/' + file); + }); + } catch (e) { + console.log('error', e); + return []; + } +}; diff --git a/seed/index.js b/seed/index.js index 8300b97245..c11360d6a7 100644 --- a/seed/index.js +++ b/seed/index.js @@ -2,29 +2,23 @@ require('babel/register'); require('dotenv').load(); -var fs = require('fs'), - Rx = require('rx'), +var Rx = require('rx'), _ = require('lodash'), - path = require('path'), + getChallenges = require('./getChallenges'), app = require('../server/server'); -function getFilesFor(dir) { - return fs.readdirSync(path.join(__dirname, '/' + dir)); -} var Challenge = app.models.Challenge; -var challenges = getFilesFor('challenges'); var destroy = Rx.Observable.fromNodeCallback(Challenge.destroyAll, Challenge); var create = Rx.Observable.fromNodeCallback(Challenge.create, Challenge); destroy() - .flatMap(function() { return Rx.Observable.from(challenges); }) - .flatMap(function(file) { - var challengeSpec = require('./challenges/' + file); + .flatMap(function() { return Rx.Observable.from(getChallenges()); }) + .flatMap(function(challengeSpec) { var order = challengeSpec.order; var block = challengeSpec.name; var isBeta = !!challengeSpec.isBeta; - console.log('parsed %s successfully', file); + console.log('parsed %s successfully', block); // challenge file has no challenges... if (challengeSpec.challenges.length === 0) { diff --git a/seed/test-challenges.js b/seed/test-challenges.js new file mode 100644 index 0000000000..e47ed22fbc --- /dev/null +++ b/seed/test-challenges.js @@ -0,0 +1,108 @@ +/* eslint-disable no-eval, no-process-exit */ +import _ from 'lodash'; +import { Observable } from 'rx'; +import tape from 'tape'; +import getChallenges from './getChallenges'; + + +function createIsAssert(t, isThing) { + const { assert } = t; + return function() { + const args = [...arguments]; + args[0] = isThing(args[0]); + assert.apply(t, args); + }; +} + +function fillAssert(t) { + const assert = t.assert; + + assert.isArray = createIsAssert(t, _.isArray); + assert.isBoolean = createIsAssert(t, _.isBoolean); + assert.isString = createIsAssert(t, _.isString); + assert.isNumber = createIsAssert(t, _.isNumber); + assert.isUndefined = createIsAssert(t, _.isUndefined); + + assert.deepEqual = t.deepEqual; + assert.equal = t.equal; + assert.strictEqual = t.equal; + + assert.sameMembers = function sameMembers() { + const [ first, second, ...args] = arguments; + assert.apply( + t, + [ + _.difference(first, second).length === 0 && + _.difference(second, first).length === 0 + ].concat(args) + ); + }; + + assert.includeMembers = function includeMembers() { + const [ first, second, ...args] = arguments; + assert.apply(t, [_.difference(second, first).length === 0].concat(args)); + }; + + assert.match = function match() { + const [value, regex, ...args] = arguments; + assert.apply(t, [regex.test(value)].concat(args)); + }; + + return assert; +} + +function createTest({ title, tests = [], solutions = [] }) { + const plan = tests.length; + return Observable.fromCallback(tape)(title) + .doOnNext(t => solutions.length ? t.plan(plan) : t.end()) + .flatMap(t => { + if (solutions.length <= 0) { + t.comment('No solutions for ' + title); + return Observable.just({ + title, + type: 'missing' + }); + } + + return Observable.just(t) + .map(fillAssert) + /* eslint-disable no-unused-vars */ + // assert is used within the eval + .doOnNext(assert => { + /* eslint-enable no-unused-vars */ + solutions.forEach(solution => { + tests.forEach(test => { + eval(solution + ';;' + test); + }); + }); + }) + .map(() => ({ title })); + }); +} + +Observable.from(getChallenges()) + .flatMap(challengeSpec => { + return Observable.from(challengeSpec.challenges); + }) + .flatMap(challenge => { + return createTest(challenge); + }) + .map(({ title, type }) => { + if (type === 'missing') { + return title; + } + return false; + }) + .filter(title => !!title) + .toArray() + .subscribe( + (noSolutions) => { + console.log( + '# These challenges have no solutions\n- [ ] ' + + noSolutions.join('\n- [ ] ') + ); + }, + err => { throw err; }, + () => process.exit(0) + ); + From af30d4d56ea19ae26db95a3c81d62b7d5d547a3a Mon Sep 17 00:00:00 2001 From: terakilobyte Date: Sun, 1 Nov 2015 17:26:26 -0800 Subject: [PATCH 31/33] Implements a 'releasedOn' flag and marks challenges less than 30 days old as NEW https://www.dropbox.com/s/43un2a908ldktpl/Screenshot%202015-11-01%2017.06.53.png --- common/models/challenge.json | 3 +++ server/boot/challenge.js | 5 +++++ server/views/challengeMap/show.jade | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/common/models/challenge.json b/common/models/challenge.json index 81d6af6eef..729cb97625 100644 --- a/common/models/challenge.json +++ b/common/models/challenge.json @@ -86,6 +86,9 @@ "solutions": { "type": "array", "default": [] + }, + "releasedOn": { + "type": "string" } }, "validations": [], diff --git a/server/boot/challenge.js b/server/boot/challenge.js index a4d057f13b..bb67b29e71 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -537,6 +537,11 @@ module.exports = function(app) { if (completedChallenges.indexOf(challenge.id) !== -1) { challenge.completed = true; } + if (typeof(challenge.releasedOn) !== 'undefined' + && moment(challenge.releasedOn, 'MMM MMMM DD, YYYY').diff(moment(), + 'days') >= -30) { + challenge.markNew = true; + } return challenge; }) // group challenges by block | returns a stream of observables diff --git a/server/views/challengeMap/show.jade b/server/views/challengeMap/show.jade index 5b4974b4e3..81e80307a1 100644 --- a/server/views/challengeMap/show.jade +++ b/server/views/challengeMap/show.jade @@ -129,6 +129,10 @@ block content span.capitalize= challenge.type + ': ' span= challenge.title span.sr-only= " Incomplete" + if challenge.markNew + span.text-danger.small     + strong + em NEW if (challengeBlock.completed === 100) .button-spacer From c30f7f0795becebfba70e0a6d9c3a8183074f475 Mon Sep 17 00:00:00 2001 From: terakilobyte Date: Sun, 1 Nov 2015 17:45:01 -0800 Subject: [PATCH 32/33] Addresses lint errors --- server/boot/challenge.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/boot/challenge.js b/server/boot/challenge.js index bb67b29e71..8f61dcbc06 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -537,8 +537,8 @@ module.exports = function(app) { if (completedChallenges.indexOf(challenge.id) !== -1) { challenge.completed = true; } - if (typeof(challenge.releasedOn) !== 'undefined' - && moment(challenge.releasedOn, 'MMM MMMM DD, YYYY').diff(moment(), + if (typeof challenge.releasedOn !== 'undefined' && + moment(challenge.releasedOn, 'MMM MMMM DD, YYYY').diff(moment(), 'days') >= -30) { challenge.markNew = true; } From 83a387b05d223f1f726c70e00eac9ce9fd8ae93b Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sun, 1 Nov 2015 21:18:00 -0800 Subject: [PATCH 33/33] Fix uncaught exceptions when testing --- seed/test-challenges.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/seed/test-challenges.js b/seed/test-challenges.js index e47ed22fbc..93a994c30c 100644 --- a/seed/test-challenges.js +++ b/seed/test-challenges.js @@ -72,7 +72,11 @@ function createTest({ title, tests = [], solutions = [] }) { /* eslint-enable no-unused-vars */ solutions.forEach(solution => { tests.forEach(test => { - eval(solution + ';;' + test); + try { + eval(solution + ';;' + test); + } catch (e) { + t.fail(e); + } }); }); })