Major refactor of bonfireFramework to differentiate tests, refactor tests in Bonfire schema to remove public/private tests and only have tests, fire modal on successful run of all tests in bonfire/show, added bonfiresHash to User schema, and we should have committed a while ago because there is too much too list

This commit is contained in:
Nathan Leniz
2015-01-24 00:44:08 -05:00
parent 37daf55719
commit 6b4475a5b8
12 changed files with 401 additions and 98 deletions

14
app.js
View File

@ -285,6 +285,20 @@ app.post('/completed-challenge', function (req, res) {
req.user.save();
});
app.post('/completed-bonfire/', function (req, res) {
req.user.challengesHash[parseInt(req.body.challengeNumber)] =
Math.round(+new Date() / 1000);
var ch = req.user.challengesHash;
var p = 0;
for (var k in ch) {
if (ch[k] > 0) {
p += 1;
}
}
req.user.points = p;
req.user.save();
});
/**
* OAuth sign-in routes.
*/

View File

@ -62,16 +62,13 @@ exports.returnBonfire = function(req, res, next) {
number: bonfire[bonfireNumber].bonfireNumber,
difficulty: bonfire[bonfireNumber].difficulty,
description: bonfire[bonfireNumber].description,
publicTests: bonfire[bonfireNumber].publicTests,
privateTests: bonfire[bonfireNumber].privateTests,
tests: bonfire[bonfireNumber].tests,
challengeSeed: bonfire[bonfireNumber].challengeSeed,
challengeEntryPoint: bonfire[bonfireNumber].challengeEntryPoint,
challengeEntryPointNegate: bonfire[bonfireNumber].challengeEntryPointNegate,
//cc: req.user ? req.user.bonfiresHash : undefined,
//points: req.user ? req.user.points : undefined,
//verb: verbs[Math.floor(Math.random() * verbs.length)],
//phrase: phrases[Math.floor(Math.random() * phrases.length)],
cc: req.user ? req.user.bonfiresHash : undefined,
points: req.user ? req.user.points : undefined,
verb: verbs[Math.floor(Math.random() * verbs.length)],
phrase: phrases[Math.floor(Math.random() * phrases.length)],
bonfires: bonfire
});
});

View File

@ -8,15 +8,13 @@ var secrets = require('../config/secrets');
var bonfireSchema = new mongoose.Schema({
name: {
type: String,
unique: true
},
difficulty: Number,
description: Array,
publicTests: Array,
privateTests: Array,
tests: Array,
challengeSeed: String,
bonfireNumber: Number,
challengeEntryPoint: String,

View File

@ -3,11 +3,8 @@ var secrets = require('../config/secrets');
var bonfireCompletionSchema = new mongoose.Schema({
dateCompleted: Number,
completedWith: String,
bonfireNumber: {
bonfireNumber: Number,
bonfireId: String
},
completedWith: ObjectId,
bonfireHash: ObjectId,
solution: String
});

View File

@ -353,7 +353,248 @@ var userSchema = new mongoose.Schema({
},
resetPasswordToken: String,
resetPasswordExpires: Date,
bonfires: Array
bonfiresHash: {
0: {
type: Boolean,
default: 0
},
1: {
type: Boolean,
default: 0
},
2: {
type: Boolean,
default: 0
},
3: {
type: Boolean,
default: 0
},
4: {
type: Boolean,
default: 0
},
5: {
type: Boolean,
default: 0
},
6: {
type: Boolean,
default: 0
},
7: {
type: Boolean,
default: 0
},
8: {
type: Boolean,
default: 0
},
9: {
type: Boolean,
default: 0
},
10: {
type: Boolean,
default: 0
},
11: {
type: Boolean,
default: 0
},
12: {
type: Boolean,
default: 0
},
13: {
type: Boolean,
default: 0
},
14: {
type: Boolean,
default: 0
},
15: {
type: Boolean,
default: 0
},
16: {
type: Boolean,
default: 0
},
17: {
type: Boolean,
default: 0
},
18: {
type: Boolean,
default: 0
},
19: {
type: Boolean,
default: 0
},
20: {
type: Boolean,
default: 0
},
21: {
type: Boolean,
default: 0
},
22: {
type: Boolean,
default: 0
},
23: {
type: Boolean,
default: 0
},
24: {
type: Boolean,
default: 0
},
25: {
type: Boolean,
default: 0
},
26: {
type: Boolean,
default: 0
},
27: {
type: Boolean,
default: 0
},
28: {
type: Boolean,
default: 0
},
29: {
type: Boolean,
default: 0
},
30: {
type: Boolean,
default: 0
},
31: {
type: Boolean,
default: 0
},
32: {
type: Boolean,
default: 0
},
33: {
type: Boolean,
default: 0
},
34: {
type: Boolean,
default: 0
},
35: {
type: Boolean,
default: 0
},
36: {
type: Boolean,
default: 0
},
37: {
type: Boolean,
default: 0
},
38: {
type: Boolean,
default: 0
},
39: {
type: Boolean,
default: 0
},
40: {
type: Boolean,
default: 0
},
41: {
type: Boolean,
default: 0
},
42: {
type: Boolean,
default: 0
},
43: {
type: Boolean,
default: 0
},
44: {
type: Boolean,
default: 0
},
45: {
type: Boolean,
default: 0
},
46: {
type: Boolean,
default: 0
},
47: {
type: Boolean,
default: 0
},
48: {
type: Boolean,
default: 0
},
49: {
type: Boolean,
default: 0
},
50: {
type: Boolean,
default: 0
},
51: {
type: Boolean,
default: 0
},
52: {
type: Boolean,
default: 0
},
53: {
type: Boolean,
default: 0
},
54: {
type: Boolean,
default: 0
},
55: {
type: Boolean,
default: 0
},
56: {
type: Boolean,
default: 0
},
57: {
type: Boolean,
default: 0
},
58: {
type: Boolean,
default: 0
},
59: {
type: Boolean,
default: 0
}
}
});
/**

View File

@ -45,9 +45,9 @@ var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"),
lineWrapping: true
});
codeOutput.setValue('/**\n' +
' * Your output will go here. Console.log() -type statements\n' +
' * will appear in your browser\'s javascript console.\n' +
' */');
' * Your output will go here. Console.log() -type statements\n' +
' * will appear in your browser\'s javascript console.\n' +
' */');
codeOutput.setSize("100%", "100%");
var info = editor.getScrollInfo();
var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, "local").top;
@ -58,10 +58,8 @@ var editorValue;
var challengeSeed = challengeSeed || null;
var publicTests = publicTests || [];
var privateTests = privateTests || [];
var tests = tests || [];
var challengeEntryPoint = challengeEntryPoint || null;
var challengeEntryPointNegate = challengeEntryPointNegate || null;
if (challengeSeed !== null) {
@ -101,7 +99,7 @@ $('#submitButton').on('click', function () {
});
function bonfireExecute() {
tests = null;
userTests= null;
$('#codeOutput').empty();
var userJavaScript = myCodeMirror.getValue();
userJavaScript = removeComments(userJavaScript);
@ -119,23 +117,20 @@ function bonfireExecute() {
}
var replaceQuotesInTests = function() {
tests.forEach(function(elt, ix, arr) {
userTests.forEach(function(elt, ix, arr) {
arr[ix].text = arr[ix].text.replace(/\"/g,'\'');
});
};
var tests;
var userTests;
var testSalt = Math.random();
var scrapeTests = function(userJavaScript) {
for (var i = 0; i < publicTests.length; i++) {
userJavaScript += '\n' + publicTests[i];
}
for (var i = 0; i < privateTests.length; i++) {
userJavaScript += '\n' + privateTests[i];
// insert tests from mongo
for (var i = 0; i < tests.length; i++) {
userJavaScript += '\n' + tests[i];
}
var counter = 0;
@ -144,16 +139,20 @@ var scrapeTests = function(userJavaScript) {
while (match != null) {
var replacement = '//' + counter + testSalt;
userJavaScript = userJavaScript.substring(0, match.index)
+ replacement
+ userJavaScript.substring(match.index + match[0].length);
+ replacement
+ userJavaScript.substring(match.index + match[0].length);
if (!tests) tests = [];
tests.push({"text": match[0], "line": counter, "err": null});
if (!userTests) {
userTests= [];
}
userTests.push({"text": match[0], "line": counter, "err": null});
counter++;
match = regex.exec(userJavaScript);
}
if (tests) replaceQuotesInTests();
if (userTests) {
replaceQuotesInTests();
}
return userJavaScript;
};
@ -170,10 +169,10 @@ function removeLogs(userJavaScript) {
var pushed = false;
var createTestDisplay = function() {
if (pushed) {
tests.pop();
userTests.pop();
}
for (var i = 0; i < tests.length;i++) {
var test = tests[i];
for (var i = 0; i < userTests.length;i++) {
var test = userTests[i];
var testDoc = document.createElement("li");
$(testDoc)
.addClass('list-group-item')
@ -201,28 +200,40 @@ var reassembleTest = function(test, data) {
var regexp = new RegExp("\/\/" + lineNum + testSalt);
return data.input.replace(regexp, test.text);
};
var runTests = function(err, data) {
var allTestsPassed = true;
pushed = false;
$('#testSuite').children().remove();
if (err && tests.length > 0) {
tests = [{text:"Program Execution Failure", err: "No tests were run."}];
if (err && userTests.length > 0) {
userTests= [{text:"Program Execution Failure", err: "NouserTestswere run."}];
createTestDisplay();
} else if (tests) {
tests.push(false);
} else if (userTests) {
userTests.push(false);
pushed = true;
tests.forEach(function(test, ix, arr){
userTests.forEach(function(test, ix, arr){
try {
if (test) {
var output = eval(reassembleTest(test, data));
}
} catch(error) {
allTestsPassed = false;
arr[ix].err = error.name + ":" + error.message;
} finally {
} finally {
if (!test) {
createTestDisplay();
}
}
});
}
if (allTestsPassed) {
allTestsPassed = false;
showCompletion();
}
console.log(allTestsPassed);
};
function showCompletion() {
$('#complete-bonfire-dialog').modal('show');
}

View File

@ -1,46 +1,64 @@
$(document).ready(function() {
var CSRF_HEADER = 'X-CSRF-Token';
var CSRF_HEADER = 'X-CSRF-Token';
var setCSRFToken = function(securityToken) {
jQuery.ajaxPrefilter(function(options, _, xhr) {
if (!xhr.crossDomain) {
xhr.setRequestHeader(CSRF_HEADER, securityToken);
}
var setCSRFToken = function(securityToken) {
jQuery.ajaxPrefilter(function(options, _, xhr) {
if (!xhr.crossDomain) {
xhr.setRequestHeader(CSRF_HEADER, securityToken);
}
});
};
setCSRFToken($('meta[name="csrf-token"]').attr('content'));
$('.start-challenge').on('click', function() {
$(this).parent().remove();
$('.challenge-content')
.removeClass('hidden-element')
.addClass('animated fadeInDown');
});
};
setCSRFToken($('meta[name="csrf-token"]').attr('content'));
$('.completed-challenge').on('click', function() {
$('#complete-challenge-dialog').modal('show');
// Only post to server if there is an authenticated user
if ($('.signup-btn-nav').length < 1) {
l = location.pathname.split('/');
cn = l[l.length - 1];
$.ajax({
type: 'POST',
data: {challengeNumber: cn},
url: '/completed-challenge/'
});
}
});
$('.start-challenge').on('click', function() {
$(this).parent().remove();
$('.challenge-content')
.removeClass('hidden-element')
.addClass('animated fadeInDown');
});
$('.completed-bonfire').on('click', function() {
$('#complete-bonfire-dialog').modal('show');
// Only post to server if there is an authenticated user
if ($('.signup-btn-nav').length < 1) {
l = location.pathname.split('/');
cn = l[l.length - 1];
$.ajax({
type: 'POST',
data: {bonfireHash: cn},
url: '/completed-bonfire/'
});
}
});
$('.completed-challenge').on('click', function() {
$('#complete-dialog').modal('show');
// Only post to server if there is an authenticated user
if ($('.signup-btn-nav').length < 1) {
l = location.pathname.split('/');
cn = l[l.length - 1];
$.ajax({
type: 'POST',
data: {challengeNumber: cn},
url: '/completed-challenge/'
});
}
});
$('.all-challenges').on('click', function() {
$('#all-challenges-dialog').modal('show');
});
$('.all-challenges').on('click', function() {
$('#all-challenges-dialog').modal('show');
});
$('.all-bonfires').on('click', function() {
$('#all-bonfires-dialog').modal('show');
});
$('.next-button').on('click', function() {
l = location.pathname.split('/');
window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1);
});
$('.next-button').on('click', function() {
l = location.pathname.split('/');
window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1);
});
});
var profileValidation = angular.module('profileValidation',['ui.bootstrap']);

View File

@ -1,5 +1,6 @@
[
{
"_id" : "aaa48de84e1ecc7c742e1124",
"name": "Palindrome Tester",
"difficulty": 1,
"description": [
@ -8,13 +9,11 @@
"Strings will be passed in with varying formats, such as \"racecar\", \"RaceCar\", and \"race CAR\" among others.",
"Return true if the string is a palindrome, otherwise false"
],
"publicTests": [
"tests": [
"expect(palindrome(\"eye\")).to.be.a(\"boolean\");",
"assert.deepEqual(palindrome(\"eye\"), true);",
"assert.deepEqual(palindrome(\"race car\"), true);",
"assert.deepEqual(palindrome(\"not a palindrome\"), false);"
],
"privateTests": [
"assert.deepEqual(palindrome(\"not a palindrome\"), false);",
"assert.deepEqual(palindrome(\"A man, a plan, a canal. Panama\"), true);",
"assert.deepEqual(palindrome(\"never odd or even\"), true);",
"assert.deepEqual(palindrome(\"nope\"), false);"
@ -25,6 +24,7 @@
"challengeEntryPointNegate" : "palindrome\\([^str].*\\;"
},
{
"_id" : "ff0395860f5d3034dc0bfc94",
"name": "Validate US Telephone Numbers",
"difficulty": 3,
"description": [
@ -32,13 +32,11 @@
"555-555-5555, (555)555-5555, (555) 555-5555, 555 555 5555, 5555555555, 1 555 555 5555",
"For this challenge you will be presented with a string such as \"800-692-7753\" or \"8oo-six427676;laskdjf\". Your job is to validate or reject the US phone number based on any combination of the formats provided above. The area code is required. If the country code code is provided, you must confirm that the country code is \"1\". Return true if the string is a valid US phone number; otherwise false."
],
"publicTests": [
"tests": [
"expect(telephoneCheck(\"555-555-5555\")).to.be.a(\"boolean\");",
"assert.deepEqual(telephoneCheck(\"555-555-5555\"), true);",
"assert.deepEqual(telephoneCheck(\"1 456 789 4444\"), true);",
"assert.deepEqual(telephoneCheck(\"123**&!!asdf#\"), false);"
],
"privateTests": [
"assert.deepEqual(telephoneCheck(\"123**&!!asdf#\"), false);",
"assert.deepEqual(telephoneCheck(\"55555555\"), false);",
"assert.deepEqual(telephoneCheck(\"(6505552368)\"), false);",
"assert.deepEqual(telephoneCheck(\"2 (757) 622-7382\"), false);",

View File

@ -1,6 +1,6 @@
/*
"aaa48de84e1ecc7c742e1124"
"ff0395860f5d3034dc0bfc94"
"7123c8c441eddfaeb5bdef0d"
"c3a4d278b9e760a0ffe8321f"
"aceca143b92049a4392a859e"

View File

@ -23,7 +23,7 @@ block content
.panel-heading.text-center #{name} (Level #{difficulty} bonfire)
.panel.panel-body
.well
.text-justify.bonfire-instructions
.text-left.bonfire-instructions
for sentence in description
p.bonfire-instructions!= sentence
form.code
@ -42,9 +42,32 @@ block content
ul#testSuite.list-group
br
script(type="text/javascript").
var publicTests = !{JSON.stringify(publicTests)};
var privateTests = !{JSON.stringify(privateTests)};
var tests = !{JSON.stringify(tests)};
var challengeSeed = !{JSON.stringify(challengeSeed)};
var challengeEntryPoint = !{JSON.stringify(challengeEntryPoint)};
var challengeEntryPointNegate = !{JSON.stringify(challengeEntryPointNegate)};
script(src='/js/lib/bonfire/bonfireFramework.js')
#complete-bonfire-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
.modal-content
.modal-header.challenge-list-header Nicely done!
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
.text-center
.animated.zoomInDown.delay-half
span.landing-icon.ion-checkmark-circled.text-primary
- if (cc)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge
- if (points && points > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20Free%20Code%20Camp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{number}&hashtags=learntocode, javascript" target="_blank")
i.fa.fa-twitter &nbsp;
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
#all-bonfires-dialog.modal(tabindex='-1')
.modal-dialog.animated.fadeInUp.fast-animation
.modal-content
.modal-header.challenge-list-header Challenges
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
include ../partials/bonfires

View File

@ -16,7 +16,7 @@ block content
.btn.btn-primary.btn-big.btn-block.completed-challenge I've completed this challenge
.ten-pixel-break
.btn.btn-success.btn-big.btn-block.all-challenges Show me all the challenges
#complete-dialog.modal(tabindex='-1')
#complete-challenge-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
.modal-content
.modal-header.challenge-list-header Nicely done!

View File

@ -1 +1,7 @@
//TODO: STUFF
h3
ol(start='0')
for bonfire in bonfires
li
a(href="/bonfires/#{bonfire.bonfireNumber}", class="#{ (cc && cc[bonfire.bonfireNumber] > 0) ? 'strikethrough' : '' }") #{bonfire.name}
| &nbsp; (Level #{bonfire.difficulty})
a.btn.btn-lg.btn-primary.btn-block(href="/done-with-first-100-hours", class="#{ ((cc && cc[53] === 0) || (!cc)) ? 'disabled' : '' }") I'm done with all the challenges!