45 changed files with 33657 additions and 0 deletions
Unified View
Diff Options
-
21d3-ex1/LICENSE.txt
-
11d3-ex1/README.md
-
73d3-ex1/karma.conf.js
-
10253d3-ex1/lib/jquery-3.2.1.js
-
270d3-ex1/mocha.css
-
1571d3-ex1/package-lock.json
-
26d3-ex1/package.json
-
5332d3-ex1/test-lib/chai.js
-
6472d3-ex1/test-lib/mocha.js
-
30d3-ex1/test.html
-
109d3-ex1/tests.js
-
54d3-ex1/utils.js
-
3d3-ex2/.bowerrc
-
43d3-ex2/.gitignore
-
28d3-ex2/Chrome_86.0.4240_(Linux_0.0.0)/test/results/unit/test-results.xml
-
33d3-ex2/Gruntfile.js
-
202d3-ex2/LICENSE
-
88d3-ex2/README.md
-
24d3-ex2/app/index.html
-
43d3-ex2/app/scripts/controllers/math.js
-
5d3-ex2/app/scripts/main.js
-
52d3-ex2/app/scripts/services/math.js
-
38d3-ex2/app/scripts/services/tools.js
-
20d3-ex2/bower.json
-
21d3-ex2/build/grunt-tasks/connect.js
-
30d3-ex2/build/grunt-tasks/test.js
-
4703d3-ex2/package-lock.json
-
60d3-ex2/package.json
-
64d3-ex2/test/config/karma.conf.js
-
29d3-ex2/test/config/karma.coverage.conf.js
-
15d3-ex2/test/config/karma.unit.conf.js
-
18d3-ex2/test/config/mocha-globals.js
-
1d3-ex2/test/specs/unit/main.js
-
104d3-ex2/test/specs/unit/services/math.spec.js
-
76d3-ex2/test/specs/unit/services/tools.spec.js
-
3d3-ex3/.gitignore
-
23d3-ex3/README.md
-
31d3-ex3/bin/postinstall.js
-
18d3-ex3/bin/preinstall.js
-
77d3-ex3/karma.conf.js
-
3275d3-ex3/package-lock.json
-
45d3-ex3/package.json
-
26d3-ex3/packagejson.old
-
68d3-ex3/src/main/webapp/app/sample.js
-
169d3-ex3/src/test/sampleTest.js
@ -0,0 +1,21 @@ |
|||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) 2015 Ludovico Fischer |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in |
||||
|
all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
THE SOFTWARE. |
||||
@ -0,0 +1,11 @@ |
|||||
|
A demo setup for unit testing client-side JavaScript code with [Mocha.js](http://mochajs.org/) and [Chai.js](http://chaijs.com/). |
||||
|
|
||||
|
Since mocha 1.2, you can install mocha via npm and run `mocha init <path>` to generate a client side testing skeleton. |
||||
|
|
||||
|
## Automated in-browser tests with Karma |
||||
|
|
||||
|
This sample also includes a [Karma](http://karma-runner.github.io/) configuration to run the tests automatically. |
||||
|
|
||||
|
To use Karma, you need to have [node.js](http://nodejs.org) and [npm](https://npmjs.org/) installed. In the project directory, type `npm install karma-mocha`. After the Karma install finishes, type `node_modules/karma/bin/karma start` and navigate to `http://localhost:9876` to let Karma capture the web browser and run the tests automatically. |
||||
|
|
||||
|
If you are familiar with `npm`, you can look at the `package.json`. That configuration allows you to just type `npm install` and then `npm test` to start the Karma server. Since the karma NPM package does not define a `bin` field, we must make use of the `karma-cli` package to be able to use the karma executable inside our `test` script. |
||||
@ -0,0 +1,73 @@ |
|||||
|
// Karma configuration
|
||||
|
// Generated on Tue Sep 02 2014 23:03:20 GMT+0200 (CEST)
|
||||
|
|
||||
|
module.exports = function(config) { |
||||
|
config.set({ |
||||
|
|
||||
|
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
|
basePath: '', |
||||
|
|
||||
|
|
||||
|
// frameworks to use
|
||||
|
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
|
frameworks: ['mocha'], |
||||
|
|
||||
|
|
||||
|
// list of files / patterns to load in the browser
|
||||
|
files: [ |
||||
|
'lib/*.js', |
||||
|
'test-lib/*.js', |
||||
|
'utils.js', |
||||
|
'tests.js' |
||||
|
], |
||||
|
|
||||
|
plugins: [ |
||||
|
'karma-chrome-launcher', |
||||
|
'karma-mocha' |
||||
|
], |
||||
|
|
||||
|
|
||||
|
// list of files to exclude
|
||||
|
exclude: [ |
||||
|
], |
||||
|
|
||||
|
|
||||
|
// preprocess matching files before serving them to the browser
|
||||
|
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
|
preprocessors: { |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// test results reporter to use
|
||||
|
// possible values: 'dots', 'progress'
|
||||
|
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
|
reporters: ['progress'], |
||||
|
|
||||
|
|
||||
|
// web server port
|
||||
|
port: 9876, |
||||
|
|
||||
|
|
||||
|
// enable / disable colors in the output (reporters and logs)
|
||||
|
colors: true, |
||||
|
|
||||
|
|
||||
|
// level of logging
|
||||
|
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
|
logLevel: config.LOG_INFO, |
||||
|
|
||||
|
|
||||
|
// enable / disable watching file and executing tests whenever any file changes
|
||||
|
autoWatch: true, |
||||
|
|
||||
|
|
||||
|
// start these browsers
|
||||
|
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
|
browsers: ['Chrome'], |
||||
|
|
||||
|
|
||||
|
// Continuous Integration mode
|
||||
|
// if true, Karma captures browsers, runs the tests and exits
|
||||
|
singleRun: false |
||||
|
}); |
||||
|
}; |
||||
10253
d3-ex1/lib/jquery-3.2.1.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,270 @@ |
|||||
|
@charset "utf-8"; |
||||
|
|
||||
|
body { |
||||
|
margin:0; |
||||
|
} |
||||
|
|
||||
|
#mocha { |
||||
|
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; |
||||
|
margin: 60px 50px; |
||||
|
} |
||||
|
|
||||
|
#mocha ul, |
||||
|
#mocha li { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
#mocha ul { |
||||
|
list-style: none; |
||||
|
} |
||||
|
|
||||
|
#mocha h1, |
||||
|
#mocha h2 { |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
#mocha h1 { |
||||
|
margin-top: 15px; |
||||
|
font-size: 1em; |
||||
|
font-weight: 200; |
||||
|
} |
||||
|
|
||||
|
#mocha h1 a { |
||||
|
text-decoration: none; |
||||
|
color: inherit; |
||||
|
} |
||||
|
|
||||
|
#mocha h1 a:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
#mocha .suite .suite h1 { |
||||
|
margin-top: 0; |
||||
|
font-size: .8em; |
||||
|
} |
||||
|
|
||||
|
#mocha .hidden { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
#mocha h2 { |
||||
|
font-size: 12px; |
||||
|
font-weight: normal; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
#mocha .suite { |
||||
|
margin-left: 15px; |
||||
|
} |
||||
|
|
||||
|
#mocha .test { |
||||
|
margin-left: 15px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.pending:hover h2::after { |
||||
|
content: '(pending)'; |
||||
|
font-family: arial, sans-serif; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.pass.medium .duration { |
||||
|
background: #c09853; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.pass.slow .duration { |
||||
|
background: #b94a48; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.pass::before { |
||||
|
content: '✓'; |
||||
|
font-size: 12px; |
||||
|
display: block; |
||||
|
float: left; |
||||
|
margin-right: 5px; |
||||
|
color: #00d6b2; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.pass .duration { |
||||
|
font-size: 9px; |
||||
|
margin-left: 5px; |
||||
|
padding: 2px 5px; |
||||
|
color: #fff; |
||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); |
||||
|
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); |
||||
|
box-shadow: inset 0 1px 1px rgba(0,0,0,.2); |
||||
|
-webkit-border-radius: 5px; |
||||
|
-moz-border-radius: 5px; |
||||
|
-ms-border-radius: 5px; |
||||
|
-o-border-radius: 5px; |
||||
|
border-radius: 5px; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.pass.fast .duration { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.pending { |
||||
|
color: #0b97c4; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.pending::before { |
||||
|
content: '◦'; |
||||
|
color: #0b97c4; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.fail { |
||||
|
color: #c00; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.fail pre { |
||||
|
color: black; |
||||
|
} |
||||
|
|
||||
|
#mocha .test.fail::before { |
||||
|
content: '✖'; |
||||
|
font-size: 12px; |
||||
|
display: block; |
||||
|
float: left; |
||||
|
margin-right: 5px; |
||||
|
color: #c00; |
||||
|
} |
||||
|
|
||||
|
#mocha .test pre.error { |
||||
|
color: #c00; |
||||
|
max-height: 300px; |
||||
|
overflow: auto; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* (1): approximate for browsers not supporting calc |
||||
|
* (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) |
||||
|
* ^^ seriously |
||||
|
*/ |
||||
|
#mocha .test pre { |
||||
|
display: block; |
||||
|
float: left; |
||||
|
clear: left; |
||||
|
font: 12px/1.5 monaco, monospace; |
||||
|
margin: 5px; |
||||
|
padding: 15px; |
||||
|
border: 1px solid #eee; |
||||
|
max-width: 85%; /*(1)*/ |
||||
|
max-width: calc(100% - 42px); /*(2)*/ |
||||
|
word-wrap: break-word; |
||||
|
border-bottom-color: #ddd; |
||||
|
-webkit-border-radius: 3px; |
||||
|
-webkit-box-shadow: 0 1px 3px #eee; |
||||
|
-moz-border-radius: 3px; |
||||
|
-moz-box-shadow: 0 1px 3px #eee; |
||||
|
border-radius: 3px; |
||||
|
} |
||||
|
|
||||
|
#mocha .test h2 { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
#mocha .test a.replay { |
||||
|
position: absolute; |
||||
|
top: 3px; |
||||
|
right: 0; |
||||
|
text-decoration: none; |
||||
|
vertical-align: middle; |
||||
|
display: block; |
||||
|
width: 15px; |
||||
|
height: 15px; |
||||
|
line-height: 15px; |
||||
|
text-align: center; |
||||
|
background: #eee; |
||||
|
font-size: 15px; |
||||
|
-moz-border-radius: 15px; |
||||
|
border-radius: 15px; |
||||
|
-webkit-transition: opacity 200ms; |
||||
|
-moz-transition: opacity 200ms; |
||||
|
transition: opacity 200ms; |
||||
|
opacity: 0.3; |
||||
|
color: #888; |
||||
|
} |
||||
|
|
||||
|
#mocha .test:hover a.replay { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
#mocha-report.pass .test.fail { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
#mocha-report.fail .test.pass { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
#mocha-report.pending .test.pass, |
||||
|
#mocha-report.pending .test.fail { |
||||
|
display: none; |
||||
|
} |
||||
|
#mocha-report.pending .test.pass.pending { |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
#mocha-error { |
||||
|
color: #c00; |
||||
|
font-size: 1.5em; |
||||
|
font-weight: 100; |
||||
|
letter-spacing: 1px; |
||||
|
} |
||||
|
|
||||
|
#mocha-stats { |
||||
|
position: fixed; |
||||
|
top: 15px; |
||||
|
right: 10px; |
||||
|
font-size: 12px; |
||||
|
margin: 0; |
||||
|
color: #888; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
|
||||
|
#mocha-stats .progress { |
||||
|
float: right; |
||||
|
padding-top: 0; |
||||
|
} |
||||
|
|
||||
|
#mocha-stats em { |
||||
|
color: black; |
||||
|
} |
||||
|
|
||||
|
#mocha-stats a { |
||||
|
text-decoration: none; |
||||
|
color: inherit; |
||||
|
} |
||||
|
|
||||
|
#mocha-stats a:hover { |
||||
|
border-bottom: 1px solid #eee; |
||||
|
} |
||||
|
|
||||
|
#mocha-stats li { |
||||
|
display: inline-block; |
||||
|
margin: 0 5px; |
||||
|
list-style: none; |
||||
|
padding-top: 11px; |
||||
|
} |
||||
|
|
||||
|
#mocha-stats canvas { |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
} |
||||
|
|
||||
|
#mocha code .comment { color: #ddd; } |
||||
|
#mocha code .init { color: #2f6fad; } |
||||
|
#mocha code .string { color: #5890ad; } |
||||
|
#mocha code .keyword { color: #8a6343; } |
||||
|
#mocha code .number { color: #2f6fad; } |
||||
|
|
||||
|
@media screen and (max-device-width: 480px) { |
||||
|
#mocha { |
||||
|
margin: 60px 0px; |
||||
|
} |
||||
|
|
||||
|
#mocha #stats { |
||||
|
position: absolute; |
||||
|
} |
||||
|
} |
||||
1571
d3-ex1/package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,26 @@ |
|||||
|
{ |
||||
|
"name": "mocha-chai-browser-demo", |
||||
|
"version": "1.0.0", |
||||
|
"description": "An example setup for JavaScript unit testing", |
||||
|
"scripts": { |
||||
|
"test": "karma start" |
||||
|
}, |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "https://github.com/ludovicofischer/mocha-chai-browser-demo.git" |
||||
|
}, |
||||
|
"author": "Ludovico Fischer", |
||||
|
"license": "MIT", |
||||
|
"bugs": { |
||||
|
"url": "https://github.com/ludovicofischer/mocha-chai-browser-demo/issues" |
||||
|
}, |
||||
|
"homepage": "https://github.com/ludovicofischer/mocha-chai-browser-demo", |
||||
|
"devDependencies": { |
||||
|
"karma": "^4.4.1", |
||||
|
"karma-chrome-launcher": "^3.1.0", |
||||
|
"karma-cli": "^2.0.0", |
||||
|
"karma-mocha": "^2.0.1", |
||||
|
"karma-mocha-reporter": "^0.3.1", |
||||
|
"mocha": "^3.4.2" |
||||
|
} |
||||
|
} |
||||
5332
d3-ex1/test-lib/chai.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
6472
d3-ex1/test-lib/mocha.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,30 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
|
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<title>Mocha Tests</title> |
||||
|
<link rel="stylesheet" href="mocha.css" /> |
||||
|
<style> |
||||
|
#fixture { |
||||
|
position: absolute; |
||||
|
top: -9999; |
||||
|
left: -9999; |
||||
|
}; |
||||
|
</style> |
||||
|
<script src="lib/jquery-3.2.1.js"></script> |
||||
|
<script src="test-lib/chai.js"></script> |
||||
|
<script src="test-lib/mocha.js"></script> |
||||
|
<script> mocha.setup('bdd');</script> |
||||
|
<script src="utils.js"></script> |
||||
|
<script src="tests.js"></script> |
||||
|
<script> |
||||
|
window.onload = function() { |
||||
|
mocha.run() |
||||
|
}; |
||||
|
</script> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="mocha"></div> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,109 @@ |
|||||
|
var expect = chai.expect; |
||||
|
var formMarkup = '<div id="fixture"><form id="testForm"> <select id="testSelect">' + |
||||
|
'<option> Hello </option> </select>' + |
||||
|
'<input type="hidden" name="contest.question1" value="some bad question"/>' + |
||||
|
'<input type="text" id="contestInput" name="contest.answer1"/>' + |
||||
|
'<input type="hidden" name="contest.question2" value="some bad question"/>' + |
||||
|
'<select name="contest.answer2"> <option>Good</option>' + |
||||
|
'<option id="contestOption">Great</option> </select>' + |
||||
|
'<input type="hidden" name="contest.question3" value="some bad question"/>' + |
||||
|
'<input type="text" id="badQuizAnswer" name="contest.answer3"/></form></div>'; |
||||
|
|
||||
|
describe('Utilities', function() { |
||||
|
'use strict'; |
||||
|
var ourQuiz; |
||||
|
var contestData = { |
||||
|
question1: 'How Are you?', |
||||
|
answer1: 'Fine, thanks.', |
||||
|
question2: 'Status', |
||||
|
answer2: 'Great', |
||||
|
question3: 'Who is the greatest painter?', |
||||
|
answer3: 'Raphael' |
||||
|
}; |
||||
|
|
||||
|
before(function() { |
||||
|
ourQuiz = new Quiz(contestData); |
||||
|
jQuery('body').append(formMarkup); |
||||
|
}); |
||||
|
|
||||
|
after(function() { |
||||
|
jQuery('#fixture').remove(); |
||||
|
}); |
||||
|
|
||||
|
it('should correctly list data', function() { |
||||
|
expect(ourQuiz.isSelect('select')).to.be.true; |
||||
|
expect(ourQuiz.isSelect('input')).to.be.false; |
||||
|
expect(ourQuiz.isInput('input')).to.be.true; |
||||
|
expect(ourQuiz.isSelect('input')).to.be.false; |
||||
|
}); |
||||
|
|
||||
|
it('should select the correct option', function() { |
||||
|
var select = document.getElementById('testSelect'); |
||||
|
ourQuiz.prefillSelect(select, 'Hello'); |
||||
|
expect(select.children[0].selected).to.be.true; |
||||
|
}); |
||||
|
|
||||
|
it('should identify matching questions', function() { |
||||
|
expect(ourQuiz.questionMatches(1, contestData.question1)).to.be.true; |
||||
|
}); |
||||
|
|
||||
|
it('should identify non-matching questions', function() { |
||||
|
expect(ourQuiz.questionMatches(1, contestData.question1 + 'blah')).to.be.false; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('Main function', function() { |
||||
|
'use strict'; |
||||
|
var ourQuiz, form; |
||||
|
var contestData = { |
||||
|
question1: 'How Are you?', |
||||
|
answer1: 'Fine, thanks.', |
||||
|
question2: 'Status', |
||||
|
answer2: 'Great', |
||||
|
question3: 'Who is the greatest painter?', |
||||
|
answer3: 'Raphael' |
||||
|
}; |
||||
|
|
||||
|
before(function() { |
||||
|
ourQuiz = new Quiz(contestData); |
||||
|
form = jQuery('body').append(formMarkup); |
||||
|
}); |
||||
|
|
||||
|
after(function() { |
||||
|
jQuery('#fixture').remove(); |
||||
|
}); |
||||
|
|
||||
|
beforeEach(function() { |
||||
|
document.getElementById('contestOption').value = contestData.answer2; |
||||
|
jQuery('input[name="contest.question1"]').val(contestData.question1); |
||||
|
jQuery('input[name="contest.question2"]').val(contestData.question2); |
||||
|
ourQuiz.prefillQuizForm(form); |
||||
|
}); |
||||
|
|
||||
|
it('should fill the text fields that correspond to an answer', function() { |
||||
|
expect(document.getElementById('contestInput').value).to.equal(contestData.answer1); |
||||
|
}); |
||||
|
|
||||
|
it('should select the options that correspond to an answer', function() { |
||||
|
expect(document.getElementById('contestOption').selected).to.be.true; |
||||
|
}); |
||||
|
|
||||
|
it('should skip fields where the question has changed', function() { |
||||
|
expect(document.getElementById('badQuizAnswer').value).to.be.empty; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('Initialization', function() { |
||||
|
'use strict'; |
||||
|
it('should initialize the main object', function() { |
||||
|
var contestData = { |
||||
|
question1: 'How Are you?', |
||||
|
answer1: 'Fine, thanks.', |
||||
|
question2: 'Status', |
||||
|
answer2: 'Great', |
||||
|
question3: 'Who is the greatest painter?', |
||||
|
answer3: 'Raphael' |
||||
|
}; |
||||
|
var contest = new Quiz(contestData); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,54 @@ |
|||||
|
(function() { |
||||
|
"use strict"; |
||||
|
var root = this; |
||||
|
var Quiz = function(contestData) { |
||||
|
this.contest = contestData; |
||||
|
}; |
||||
|
|
||||
|
Quiz.prototype.isSelect = function(tagName) { |
||||
|
return tagName.toUpperCase() === 'SELECT'; |
||||
|
}; |
||||
|
Quiz.prototype.isInput = function(tagName) { |
||||
|
return tagName.toUpperCase() === 'INPUT'; |
||||
|
}; |
||||
|
Quiz.prototype.prefillInput = function(input, text) { |
||||
|
input.value = text; |
||||
|
}; |
||||
|
|
||||
|
Quiz.prototype.prefillSelect = function(select, text) { |
||||
|
jQuery('option', select).each(function() { |
||||
|
if (this.innerHTML === text) { |
||||
|
this.selected = true; |
||||
|
}; |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
Quiz.prototype.prefillField = function(field, text) { |
||||
|
if (this.isInput(field.tagName)) { |
||||
|
this.prefillInput(field, text); |
||||
|
} else if (this.isSelect(field.tagName)) { |
||||
|
this.prefillSelect(field, text); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Quiz.prototype.questionMatches = function(questionIndex, text) { |
||||
|
var questionProperty = "question" + questionIndex; |
||||
|
return this.contest[questionProperty] === text; |
||||
|
}; |
||||
|
|
||||
|
Quiz.prototype.prefillQuizForm = function(form) { |
||||
|
var numberOfQuestions = 5; |
||||
|
for (var i = 1; i <= numberOfQuestions; i++) { |
||||
|
var answerName = 'contest.answer' + i; |
||||
|
var questionName = 'contest.question' + i; |
||||
|
var contestProperty = "answer" + i; |
||||
|
var _this = this; |
||||
|
jQuery('[name="' + answerName + '"]', form).each(function() { |
||||
|
if (_this.questionMatches(i, jQuery('input[name="'+ questionName + '"]', form).val())) { |
||||
|
_this.prefillField(this, _this.contest[contestProperty]); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
root.Quiz = Quiz; |
||||
|
}).call(this); |
||||
@ -0,0 +1,3 @@ |
|||||
|
{ |
||||
|
"directory": "app/bower-components" |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
# ---------------------------- |
||||
|
# --- GIT ignore --- |
||||
|
# ---------------------------- |
||||
|
|
||||
|
# Untracked general purpose files: |
||||
|
.project |
||||
|
.sass-cache |
||||
|
.settings |
||||
|
.jscsrc |
||||
|
*.sublime-* |
||||
|
*.seed |
||||
|
*.log |
||||
|
*.csv |
||||
|
*.dat |
||||
|
*.out |
||||
|
*.pid |
||||
|
*.gz |
||||
|
*.bck |
||||
|
*.tmp |
||||
|
*.orig |
||||
|
/npm-debug.log |
||||
|
|
||||
|
# Untracked tmp files |
||||
|
# For Mac |
||||
|
.DS_Store |
||||
|
|
||||
|
# Untracked general purpose directories: |
||||
|
.idea |
||||
|
lib-cov |
||||
|
pids |
||||
|
logs |
||||
|
doc |
||||
|
jsdoc |
||||
|
publish |
||||
|
dist |
||||
|
target |
||||
|
tmp |
||||
|
temp |
||||
|
/node_modules |
||||
|
|
||||
|
# Untracked application files and directories: |
||||
|
/app/bower-components |
||||
|
test/results |
||||
@ -0,0 +1,28 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<testsuite name="Chrome 86.0.4240 (Linux 0.0.0)" package="" timestamp="2020-10-26T06:08:41" id="0" hostname="osboxes" tests="17" errors="0" failures="0" time="0.045"> |
||||
|
<properties> |
||||
|
<property name="browser.fullName" value="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36"/> |
||||
|
</properties> |
||||
|
<testcase name="services:math should be defined an be an object" time="0.004" classname="Chrome_86_0_4240_(Linux_0_0_0).services:math"/> |
||||
|
<testcase name="services:math should have the next methods and properties" time="0.002" classname="Chrome_86_0_4240_(Linux_0_0_0).services:math"/> |
||||
|
<testcase name="services:math method div should return a number" time="0.009" classname="Chrome_86_0_4240_(Linux_0_0_0).services:math method div"/> |
||||
|
<testcase name="services:math method div should return the result of the division" time="0" classname="Chrome_86_0_4240_(Linux_0_0_0).services:math method div"/> |
||||
|
<testcase name="services:math method div should throw an exception if the second parameter is '0'" time="0" classname="Chrome_86_0_4240_(Linux_0_0_0).services:math method div"/> |
||||
|
<testcase name="services:math method add should return nothing" time="0.001" classname="Chrome_86_0_4240_(Linux_0_0_0).services:math method add"/> |
||||
|
<testcase name="services:math method add should add the current value to the total one" time="0.001" classname="Chrome_86_0_4240_(Linux_0_0_0).services:math method add"/> |
||||
|
<testcase name="services:math method addRandom should add the current value to the total one using promise" time="0.004" classname="Chrome_86_0_4240_(Linux_0_0_0).services:math method addRandom"/> |
||||
|
<testcase name="services:tools should be defined an be an object" time="0.001" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools"/> |
||||
|
<testcase name="services:tools should have the next methods and properties" time="0.001" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools"/> |
||||
|
<testcase name="services:tools method sum(*, *) should return a number" time="0" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools method sum(*, *)"/> |
||||
|
<testcase name="services:tools method sum(*, *) should return the addition of integer and float numbers" time="0.001" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools method sum(*, *)"/> |
||||
|
<testcase name="services:tools method sum(*, *) should return the addition of numbers in string format" time="0" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools method sum(*, *)"/> |
||||
|
<testcase name="services:tools method sum(*, *) should return '0' if the parameters are missing" time="0" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools method sum(*, *)"/> |
||||
|
<testcase name="services:tools method sum(*, *) should use '0' as default value if the parameter is missing" time="0" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools method sum(*, *)"/> |
||||
|
<testcase name="services:tools method sum(*, *) should use '0' as default value if the parameter is not a number nor a string-number" time="0" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools method sum(*, *)"/> |
||||
|
<testcase name="services:tools method getRandomNumbers should return a random number" time="0.021" classname="Chrome_86_0_4240_(Linux_0_0_0).services:tools method getRandomNumbers"/> |
||||
|
<system-out> |
||||
|
<![CDATA[ |
||||
|
]]> |
||||
|
</system-out> |
||||
|
<system-err/> |
||||
|
</testsuite> |
||||
@ -0,0 +1,33 @@ |
|||||
|
/** |
||||
|
* Register Grunt tasks to automate workflow. |
||||
|
* |
||||
|
* Exposes the following tasks: |
||||
|
* grunt |
||||
|
* |
||||
|
* @param {Object} grunt Grunt configuration. |
||||
|
*/ |
||||
|
module.exports = function(grunt) { |
||||
|
var timer = require('grunt-timer'); |
||||
|
|
||||
|
// Timer init to show execute time of grunt task.
|
||||
|
timer.init(grunt); |
||||
|
|
||||
|
grunt.initConfig({ |
||||
|
pkg: grunt.file.readJSON('package.json'), |
||||
|
source: { |
||||
|
app: 'app', |
||||
|
build: 'build', |
||||
|
doc: 'doc', |
||||
|
test: 'test', |
||||
|
grunt: 'Gruntfile.js' |
||||
|
}, |
||||
|
server: { |
||||
|
host: 'localhost', |
||||
|
port: '9000' |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
grunt.loadTasks('build/grunt-tasks'); |
||||
|
|
||||
|
grunt.registerTask('default', ['connect:dev']); |
||||
|
}; |
||||
@ -0,0 +1,202 @@ |
|||||
|
Apache License |
||||
|
Version 2.0, January 2004 |
||||
|
http://www.apache.org/licenses/ |
||||
|
|
||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
|
||||
|
1. Definitions. |
||||
|
|
||||
|
"License" shall mean the terms and conditions for use, reproduction, |
||||
|
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
|
||||
|
"Licensor" shall mean the copyright owner or entity authorized by |
||||
|
the copyright owner that is granting the License. |
||||
|
|
||||
|
"Legal Entity" shall mean the union of the acting entity and all |
||||
|
other entities that control, are controlled by, or are under common |
||||
|
control with that entity. For the purposes of this definition, |
||||
|
"control" means (i) the power, direct or indirect, to cause the |
||||
|
direction or management of such entity, whether by contract or |
||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
|
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
|
||||
|
"You" (or "Your") shall mean an individual or Legal Entity |
||||
|
exercising permissions granted by this License. |
||||
|
|
||||
|
"Source" form shall mean the preferred form for making modifications, |
||||
|
including but not limited to software source code, documentation |
||||
|
source, and configuration files. |
||||
|
|
||||
|
"Object" form shall mean any form resulting from mechanical |
||||
|
transformation or translation of a Source form, including but |
||||
|
not limited to compiled object code, generated documentation, |
||||
|
and conversions to other media types. |
||||
|
|
||||
|
"Work" shall mean the work of authorship, whether in Source or |
||||
|
Object form, made available under the License, as indicated by a |
||||
|
copyright notice that is included in or attached to the work |
||||
|
(an example is provided in the Appendix below). |
||||
|
|
||||
|
"Derivative Works" shall mean any work, whether in Source or Object |
||||
|
form, that is based on (or derived from) the Work and for which the |
||||
|
editorial revisions, annotations, elaborations, or other modifications |
||||
|
represent, as a whole, an original work of authorship. For the purposes |
||||
|
of this License, Derivative Works shall not include works that remain |
||||
|
separable from, or merely link (or bind by name) to the interfaces of, |
||||
|
the Work and Derivative Works thereof. |
||||
|
|
||||
|
"Contribution" shall mean any work of authorship, including |
||||
|
the original version of the Work and any modifications or additions |
||||
|
to that Work or Derivative Works thereof, that is intentionally |
||||
|
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
|
or by an individual or Legal Entity authorized to submit on behalf of |
||||
|
the copyright owner. For the purposes of this definition, "submitted" |
||||
|
means any form of electronic, verbal, or written communication sent |
||||
|
to the Licensor or its representatives, including but not limited to |
||||
|
communication on electronic mailing lists, source code control systems, |
||||
|
and issue tracking systems that are managed by, or on behalf of, the |
||||
|
Licensor for the purpose of discussing and improving the Work, but |
||||
|
excluding communication that is conspicuously marked or otherwise |
||||
|
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
|
||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
|
on behalf of whom a Contribution has been received by Licensor and |
||||
|
subsequently incorporated within the Work. |
||||
|
|
||||
|
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
copyright license to reproduce, prepare Derivative Works of, |
||||
|
publicly display, publicly perform, sublicense, and distribute the |
||||
|
Work and such Derivative Works in Source or Object form. |
||||
|
|
||||
|
3. Grant of Patent License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
(except as stated in this section) patent license to make, have made, |
||||
|
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
|
where such license applies only to those patent claims licensable |
||||
|
by such Contributor that are necessarily infringed by their |
||||
|
Contribution(s) alone or by combination of their Contribution(s) |
||||
|
with the Work to which such Contribution(s) was submitted. If You |
||||
|
institute patent litigation against any entity (including a |
||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
|
or a Contribution incorporated within the Work constitutes direct |
||||
|
or contributory patent infringement, then any patent licenses |
||||
|
granted to You under this License for that Work shall terminate |
||||
|
as of the date such litigation is filed. |
||||
|
|
||||
|
4. Redistribution. You may reproduce and distribute copies of the |
||||
|
Work or Derivative Works thereof in any medium, with or without |
||||
|
modifications, and in Source or Object form, provided that You |
||||
|
meet the following conditions: |
||||
|
|
||||
|
(a) You must give any other recipients of the Work or |
||||
|
Derivative Works a copy of this License; and |
||||
|
|
||||
|
(b) You must cause any modified files to carry prominent notices |
||||
|
stating that You changed the files; and |
||||
|
|
||||
|
(c) You must retain, in the Source form of any Derivative Works |
||||
|
that You distribute, all copyright, patent, trademark, and |
||||
|
attribution notices from the Source form of the Work, |
||||
|
excluding those notices that do not pertain to any part of |
||||
|
the Derivative Works; and |
||||
|
|
||||
|
(d) If the Work includes a "NOTICE" text file as part of its |
||||
|
distribution, then any Derivative Works that You distribute must |
||||
|
include a readable copy of the attribution notices contained |
||||
|
within such NOTICE file, excluding those notices that do not |
||||
|
pertain to any part of the Derivative Works, in at least one |
||||
|
of the following places: within a NOTICE text file distributed |
||||
|
as part of the Derivative Works; within the Source form or |
||||
|
documentation, if provided along with the Derivative Works; or, |
||||
|
within a display generated by the Derivative Works, if and |
||||
|
wherever such third-party notices normally appear. The contents |
||||
|
of the NOTICE file are for informational purposes only and |
||||
|
do not modify the License. You may add Your own attribution |
||||
|
notices within Derivative Works that You distribute, alongside |
||||
|
or as an addendum to the NOTICE text from the Work, provided |
||||
|
that such additional attribution notices cannot be construed |
||||
|
as modifying the License. |
||||
|
|
||||
|
You may add Your own copyright statement to Your modifications and |
||||
|
may provide additional or different license terms and conditions |
||||
|
for use, reproduction, or distribution of Your modifications, or |
||||
|
for any such Derivative Works as a whole, provided Your use, |
||||
|
reproduction, and distribution of the Work otherwise complies with |
||||
|
the conditions stated in this License. |
||||
|
|
||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
|
any Contribution intentionally submitted for inclusion in the Work |
||||
|
by You to the Licensor shall be under the terms and conditions of |
||||
|
this License, without any additional terms or conditions. |
||||
|
Notwithstanding the above, nothing herein shall supersede or modify |
||||
|
the terms of any separate license agreement you may have executed |
||||
|
with Licensor regarding such Contributions. |
||||
|
|
||||
|
6. Trademarks. This License does not grant permission to use the trade |
||||
|
names, trademarks, service marks, or product names of the Licensor, |
||||
|
except as required for reasonable and customary use in describing the |
||||
|
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
|
||||
|
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
|
agreed to in writing, Licensor provides the Work (and each |
||||
|
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
|
implied, including, without limitation, any warranties or conditions |
||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
|
appropriateness of using or redistributing the Work and assume any |
||||
|
risks associated with Your exercise of permissions under this License. |
||||
|
|
||||
|
8. Limitation of Liability. In no event and under no legal theory, |
||||
|
whether in tort (including negligence), contract, or otherwise, |
||||
|
unless required by applicable law (such as deliberate and grossly |
||||
|
negligent acts) or agreed to in writing, shall any Contributor be |
||||
|
liable to You for damages, including any direct, indirect, special, |
||||
|
incidental, or consequential damages of any character arising as a |
||||
|
result of this License or out of the use or inability to use the |
||||
|
Work (including but not limited to damages for loss of goodwill, |
||||
|
work stoppage, computer failure or malfunction, or any and all |
||||
|
other commercial damages or losses), even if such Contributor |
||||
|
has been advised of the possibility of such damages. |
||||
|
|
||||
|
9. Accepting Warranty or Additional Liability. While redistributing |
||||
|
the Work or Derivative Works thereof, You may choose to offer, |
||||
|
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
|
or other liability obligations and/or rights consistent with this |
||||
|
License. However, in accepting such obligations, You may act only |
||||
|
on Your own behalf and on Your sole responsibility, not on behalf |
||||
|
of any other Contributor, and only if You agree to indemnify, |
||||
|
defend, and hold each Contributor harmless for any liability |
||||
|
incurred by, or claims asserted against, such Contributor by reason |
||||
|
of your accepting any such warranty or additional liability. |
||||
|
|
||||
|
END OF TERMS AND CONDITIONS |
||||
|
|
||||
|
APPENDIX: How to apply the Apache License to your work. |
||||
|
|
||||
|
To apply the Apache License to your work, attach the following |
||||
|
boilerplate notice, with the fields enclosed by brackets "{}" |
||||
|
replaced with your own identifying information. (Don't include |
||||
|
the brackets!) The text should be enclosed in the appropriate |
||||
|
comment syntax for the file format. We also recommend that a |
||||
|
file or class name and description of purpose be included on the |
||||
|
same "printed page" as the copyright notice for easier |
||||
|
identification within third-party archives. |
||||
|
|
||||
|
Copyright {yyyy} {name of copyright owner} |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
@ -0,0 +1,88 @@ |
|||||
|
# JavaScript testing |
||||
|
|
||||
|
The goal of this example is to know how to write tests for Javascript projects. |
||||
|
- Configure the tools. |
||||
|
- Run the tests in a non-UI browser: [PhantomJS](http://phantomjs.org/). |
||||
|
- Mocking the dependencies. |
||||
|
- Replace server requests using _Sinon_. |
||||
|
- Debugging the tests and its source-code using _Chrome dev tools_. |
||||
|
|
||||
|
## Tools |
||||
|
|
||||
|
- [Karma](https://karma-runner.github.io/0.13/index.html): this test-runner can be different configurations for the different types of tests. |
||||
|
- The runner is configured to use the Mocha framework, a preferred browser, the test output format -junit- and other features such as coverage results. |
||||
|
- The preferred browser is PhantomJS because is a non-UI browser which can be used with the continuous-integration tool. |
||||
|
Other browsers: almost any commercial browser can be used as executor: Firefox, Chrome, Safari, etc. |
||||
|
- [Mocha](http://mochajs.org/): test framework running on NodeJS and the browser that allows to make asynchronous testing easily. |
||||
|
- [Chai](http://chaijs.com/): the assertion library, is compatible with Mocha and allows to choose a TDD/BDD assert types. |
||||
|
- [Expect](http://chaijs.com/api/bdd/): assertion methods for BDD expectations. Expect has also been choosed because it does not modify the object prototype. |
||||
|
- [Sinon](http://sinonjs.org/docs/): used for spying, creating stubs and mocking the tests, is very powerful and with Sinon-Chai library is fully compatible with Chai expectations. |
||||
|
- [Sinon-Chai](http://chaijs.com/plugins/sinon-chai): extension of Chai expectations to be used with Sinon. |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
Make sure this is installed first: |
||||
|
- [Node.js](https://nodejs.org/en/) |
||||
|
|
||||
|
The next step is install the Grunt and Bower globally if they are not installed yet: |
||||
|
> npm install -g grunt bower |
||||
|
|
||||
|
Install dependencies: |
||||
|
> npm install |
||||
|
> bower install |
||||
|
|
||||
|
## Running tests |
||||
|
|
||||
|
[Grunt](http://gruntjs.com/) is the tool used to run the tests, it will launch _karma_ with its different configurations. |
||||
|
|
||||
|
Available grunt task: |
||||
|
> grunt test:unit |
||||
|
> grunt test:coverage |
||||
|
|
||||
|
### Debugging |
||||
|
|
||||
|
In order to debug in _Chrome_ for example, the _karma_ base configuration has to be updated, ``karma.conf.js``: |
||||
|
|
||||
|
- ``browsers``: browsers which will be opened to run the tests, more than one could be choosen at a time. |
||||
|
- ``singleRun``: if ``true`` the browser will be stopped after the first execution, else the browser will keep alive and listening for file modifications to run the tests automatically. |
||||
|
- For continuous integration the value should be: ``true``. |
||||
|
|
||||
|
```js |
||||
|
// Start these browsers, currently available: |
||||
|
// - Chrome |
||||
|
// - ChromeCanary |
||||
|
// - Firefox |
||||
|
// - Opera |
||||
|
// - Safari (only Mac) |
||||
|
// - PhantomJS |
||||
|
// - IE (only Windows) |
||||
|
browsers: [ |
||||
|
'Chrome' |
||||
|
], |
||||
|
|
||||
|
// Continuous Integration mode |
||||
|
// if true, it capture browsers, run tests and exit |
||||
|
singleRun: false |
||||
|
``` |
||||
|
|
||||
|
## Coverage |
||||
|
|
||||
|
The coverage analysis is written directly in the console, however a more detailed one can be found in the folder: ``test/results/coverage``. |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
``` |
||||
|
----------------------|----------|----------|----------|----------|----------------| |
||||
|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines | |
||||
|
----------------------|----------|----------|----------|----------|----------------| |
||||
|
scripts\ | 100 | 100 | 100 | 100 | | |
||||
|
main.js | 100 | 100 | 100 | 100 | | |
||||
|
scripts\controllers\ | 50 | 100 | 33.33 | 50 | | |
||||
|
math.js | 50 | 100 | 33.33 | 50 |... 30,32,37,38 | |
||||
|
scripts\services\ | 100 | 100 | 100 | 100 | | |
||||
|
math.js | 100 | 100 | 100 | 100 | | |
||||
|
tools.js | 100 | 100 | 100 | 100 | | |
||||
|
----------------------|----------|----------|----------|----------|----------------| |
||||
|
All files | 78.57 | 100 | 73.33 | 78.57 | | |
||||
|
----------------------|----------|----------|----------|----------|----------------| |
||||
|
``` |
||||
@ -0,0 +1,24 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<title>Math</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>Math application</h1> |
||||
|
<h2>Total: <span id="total"></span></h2> |
||||
|
<form id="addFrom"> |
||||
|
<input id="numberField" type="text" size="3" /> |
||||
|
<button id="addButton" type="submit" disabled>Add</button> |
||||
|
</form> |
||||
|
<p> |
||||
|
<button id="addRandomButton" type="button">Add Random Number</button> |
||||
|
</p> |
||||
|
<script src="bower-components/jquery/dist/jquery.js"></script> |
||||
|
<script src="bower-components/loglevel/dist/loglevel.js"></script> |
||||
|
|
||||
|
<script src="scripts/services/tools.js"></script> |
||||
|
<script src="scripts/services/math.js"></script> |
||||
|
<script src="scripts/controllers/math.js"></script> |
||||
|
<script src="scripts/main.js"></script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,43 @@ |
|||||
|
(function(jQuery, math, log) { |
||||
|
var addButton = jQuery('#addButton'), |
||||
|
numberInput = jQuery('#numberField'), |
||||
|
randomButton = jQuery('#addRandomButton'), |
||||
|
total = jQuery('#total'), |
||||
|
logger = log.getLogger('indexController'); |
||||
|
|
||||
|
logger.info('Controller math started'); |
||||
|
|
||||
|
/** |
||||
|
* Render value as total. |
||||
|
* @param {string|number} value - value to render. |
||||
|
*/ |
||||
|
function renderTotal(value) { |
||||
|
total.html(value); |
||||
|
} |
||||
|
|
||||
|
// Enable add button if the input is a number.
|
||||
|
numberInput.on('keyup change', function(event) { |
||||
|
numberInput.val(event.target.value.replace(/[^0-9]/g, '')); |
||||
|
addButton.prop('disabled', !parseFloat(event.target.value)); |
||||
|
}); |
||||
|
|
||||
|
// Add the number to the total.
|
||||
|
addButton.click(function(event) { |
||||
|
math.add(numberInput.val()); |
||||
|
renderTotal(math.total); |
||||
|
|
||||
|
numberInput.val(''); |
||||
|
addButton.prop('disabled', true); |
||||
|
|
||||
|
event.preventDefault(); |
||||
|
}); |
||||
|
|
||||
|
// Generate a random number and update the total.
|
||||
|
randomButton.click(function() { |
||||
|
math.addRandom().then(function() { |
||||
|
renderTotal(math.total); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
renderTotal(this.math.total); |
||||
|
}(window.jQuery, window.math, window.log)); |
||||
@ -0,0 +1,5 @@ |
|||||
|
(function(log) { |
||||
|
var logger = log.getLogger('main'); |
||||
|
|
||||
|
logger.info('Application started!'); |
||||
|
}(window.log)); |
||||
@ -0,0 +1,52 @@ |
|||||
|
(function(window, log) { |
||||
|
var logger = log.getLogger('mathService'); |
||||
|
|
||||
|
logger.info('Service math started'); |
||||
|
|
||||
|
this.math = { |
||||
|
|
||||
|
/** |
||||
|
* @prop {number} total - the total value |
||||
|
*/ |
||||
|
total: 0, |
||||
|
|
||||
|
/** |
||||
|
* Divide number a on b and return result |
||||
|
* @param {number} a - divider |
||||
|
* @param {number} b - divisor |
||||
|
* @return {number} - result |
||||
|
*/ |
||||
|
div: function(a, b) { |
||||
|
var result = 0; |
||||
|
|
||||
|
if (b) { |
||||
|
result = a / b; |
||||
|
} else { |
||||
|
throw Error('divisor-zero'); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Add a number to the total |
||||
|
* @param {number} n - a number add to |
||||
|
*/ |
||||
|
add: function(n) { |
||||
|
this.total = window.tools.sum(this.total, n); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Add random number to the total |
||||
|
* @return {Promise} - jQuery promise |
||||
|
*/ |
||||
|
addRandom: function() { |
||||
|
var _this = this; |
||||
|
|
||||
|
return window.tools.getRandomNumbers().then(function(random) { |
||||
|
logger.debug('Generated randon value: ', random); |
||||
|
_this.add(random); |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
}(window, window.log)); |
||||
@ -0,0 +1,38 @@ |
|||||
|
(function(jQuery, log) { |
||||
|
var logger = log.getLogger('toolsService'); |
||||
|
|
||||
|
logger.info('Service tools started'); |
||||
|
|
||||
|
this.tools = { |
||||
|
|
||||
|
/** |
||||
|
* Make sum of two numbers. |
||||
|
* @param {*} a - first number to sum. |
||||
|
* @param {*} b - second number to sum. |
||||
|
* @return {number} - result of sum. |
||||
|
*/ |
||||
|
sum: function(a, b) { |
||||
|
return (parseFloat(a) || 0) + (parseFloat(b) || 0); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Get random number by using service random.org. |
||||
|
* @param {number} [quantity=1] - count of numbers to get. |
||||
|
* @return {Promise} - jQuery promise. |
||||
|
*/ |
||||
|
getRandomNumbers: function() { |
||||
|
var url = 'https://www.random.org/integers/?num=10&min=1&max=6&col=1' + |
||||
|
'&base=10&format=plain&rnd=new'; |
||||
|
|
||||
|
return jQuery.getJSON(url, { |
||||
|
num: 1, |
||||
|
min: 1, |
||||
|
max: 100, |
||||
|
col: 1, |
||||
|
base: 10, |
||||
|
format: 'plain', |
||||
|
rnd: 'new' |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
}(window.jQuery, window.log)); |
||||
@ -0,0 +1,20 @@ |
|||||
|
{ |
||||
|
"name": "javascript-testing", |
||||
|
"version": "1.0.0", |
||||
|
"authors": [ |
||||
|
"jpastorp" |
||||
|
], |
||||
|
"ignore": [ |
||||
|
"**/.*", |
||||
|
"node_modules", |
||||
|
"app/bower-components", |
||||
|
"test" |
||||
|
], |
||||
|
"devDependencies": { |
||||
|
"sinon": "1.17.2" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"jquery": "2.1.4", |
||||
|
"loglevel": "1.4.0" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
/** |
||||
|
* Start the server. Registered tasks: |
||||
|
* - connect |
||||
|
* - run |
||||
|
* |
||||
|
* @param {Object} grunt Reference to the current Grunt process. |
||||
|
*/ |
||||
|
module.exports = function(grunt) { |
||||
|
grunt.loadNpmTasks('grunt-contrib-connect'); |
||||
|
|
||||
|
grunt.config.set('connect', { |
||||
|
dev: { |
||||
|
options: { |
||||
|
base: '<%= source.app %>', |
||||
|
port: '<%= server.port %>', |
||||
|
open: false, |
||||
|
keepalive: true |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,30 @@ |
|||||
|
/** |
||||
|
* Run the tests. Registered tasks: |
||||
|
* - test |
||||
|
* - test:unit |
||||
|
* - test:functional |
||||
|
* - test:coverage |
||||
|
* |
||||
|
* @param {Object} grunt Reference to the current Grunt process. |
||||
|
*/ |
||||
|
module.exports = function(grunt) { |
||||
|
grunt.loadNpmTasks('grunt-karma'); |
||||
|
|
||||
|
grunt.config.set('karma', { |
||||
|
unit: { |
||||
|
options: { |
||||
|
configFile: './<%= source.test %>/config/karma.unit.conf.js' |
||||
|
} |
||||
|
}, |
||||
|
coverage: { |
||||
|
options: { |
||||
|
configFile: './<%= source.test %>/config/karma.coverage.conf.js' |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Register the test tasks.
|
||||
|
grunt.registerTask('test:unit', ['karma:unit']); |
||||
|
grunt.registerTask('test:coverage', ['karma:coverage']); |
||||
|
grunt.registerTask('test', ['test:unit']); |
||||
|
}; |
||||
4703
d3-ex2/package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,60 @@ |
|||||
|
{ |
||||
|
"name": "javascript-testing", |
||||
|
"version": "1.0.0", |
||||
|
"description": "Basic testing example for Javascript projects with karma, mocha, chai, sinon and phantomjs", |
||||
|
"homepage": "https://github.com/moelders/javascript-testing/", |
||||
|
"main": "grunt", |
||||
|
"scripts": { |
||||
|
"test": "grunt test" |
||||
|
}, |
||||
|
"author": { |
||||
|
"name": "Jaime Pastor Pueyo", |
||||
|
"email": "moelders@gmail.com" |
||||
|
}, |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "https://github.com/moelders/javascript-testing.git" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"sinon-chai": "^3.5.0", |
||||
|
"sinon": "^9.2.0", |
||||
|
"grunt": "^1.3.0", |
||||
|
"karma": "^4.4.1", |
||||
|
"mocha": "^8.2.0", |
||||
|
"chai": "^4.2.0", |
||||
|
"karma-chai": "0.1.0", |
||||
|
"grunt-contrib-connect": "3.0.0", |
||||
|
"grunt-karma": "4.0.0", |
||||
|
"grunt-run": "0.8.1", |
||||
|
"grunt-timer": "0.6.0", |
||||
|
"karma-chai-sinon": "0.1.5", |
||||
|
"karma-chrome-launcher": "3.1.0", |
||||
|
"karma-mocha": "2.0.1", |
||||
|
"karma-junit-reporter": "2.0.1", |
||||
|
"karma-sinon": "1.0.5", |
||||
|
"karma-coverage": "^2.0.3" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=0.10.0" |
||||
|
}, |
||||
|
"license": { |
||||
|
"type": "Apache-2.0", |
||||
|
"description": "Apache License Version 2.0, January 2004", |
||||
|
"url": "http://www.apache.org/licenses/LICENSE-2.0" |
||||
|
}, |
||||
|
"keywords": [ |
||||
|
"test", |
||||
|
"testing", |
||||
|
"unit", |
||||
|
"coverage", |
||||
|
"karma", |
||||
|
"mocha", |
||||
|
"chai", |
||||
|
"sinon", |
||||
|
"phantomjs" |
||||
|
], |
||||
|
"dependencies": { |
||||
|
"bower": "^1.8.8", |
||||
|
"install": "^0.13.0" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
// Karma shared configuration
|
||||
|
|
||||
|
module.exports = function(config) { |
||||
|
config.set({ |
||||
|
|
||||
|
// base path, that will be used to resolve files and exclude
|
||||
|
basePath: '../../', |
||||
|
|
||||
|
// list of files / patterns to load in the browser
|
||||
|
files: [ |
||||
|
'app/bower-components/jquery/dist/jquery.js', |
||||
|
'app/bower-components/loglevel/dist/loglevel.js', |
||||
|
|
||||
|
'app/scripts/services/**/*.js', |
||||
|
'app/scripts/controllers/**/*.js', |
||||
|
'app/scripts/main.js', |
||||
|
|
||||
|
'test/specs/unit/**/*.spec.js', |
||||
|
|
||||
|
'test/config/mocha-globals.js', |
||||
|
], |
||||
|
|
||||
|
// list of files / patterns to exclude
|
||||
|
exclude: [], |
||||
|
|
||||
|
/* Enable / disable watching file and executing tests |
||||
|
* whenever any file changes |
||||
|
*/ |
||||
|
autoWatch: true, |
||||
|
|
||||
|
// testing framework to use (jasmine/mocha/qunit/...)
|
||||
|
// as well as any additional frameworks (requirejs/chai/sinon/...)
|
||||
|
frameworks: [ |
||||
|
'mocha', |
||||
|
'chai', |
||||
|
'sinon', |
||||
|
'chai-sinon' |
||||
|
], |
||||
|
|
||||
|
// Shared plugins (default is 'karma-*'):
|
||||
|
plugins: [ |
||||
|
'karma-*' |
||||
|
], |
||||
|
|
||||
|
// web server port
|
||||
|
port: 8080, |
||||
|
|
||||
|
// Start these browsers, currently available:
|
||||
|
// - Chrome
|
||||
|
// - ChromeCanary
|
||||
|
// - Firefox
|
||||
|
// - Opera
|
||||
|
// - Safari (only Mac)
|
||||
|
// - PhantomJS
|
||||
|
// - IE (only Windows)
|
||||
|
browsers: [ |
||||
|
'Chrome' |
||||
|
], |
||||
|
|
||||
|
// Continuous Integration mode
|
||||
|
// if true, it capture browsers, run tests and exit
|
||||
|
singleRun: true |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,29 @@ |
|||||
|
// Karma coverage configuration
|
||||
|
|
||||
|
module.exports = function(config) { |
||||
|
var baseConfig = require('../config/karma.conf'); |
||||
|
|
||||
|
baseConfig(config); |
||||
|
|
||||
|
config.set({ |
||||
|
preprocessors: { |
||||
|
'app/scripts/**/*.js': ['coverage'] |
||||
|
}, |
||||
|
|
||||
|
reporters: ['coverage'], |
||||
|
|
||||
|
coverageReporter: { |
||||
|
reporters: [ |
||||
|
{ |
||||
|
type: 'text' |
||||
|
}, { |
||||
|
type: 'lcov', |
||||
|
dir: 'test/results/coverage', |
||||
|
subdir: function(browserName) { |
||||
|
return browserName.toLowerCase().split(/[\s/-]/)[0]; |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Karma configuration for unit-test
|
||||
|
|
||||
|
module.exports = function(config) { |
||||
|
var baseConfig = require('../config/karma.conf'); |
||||
|
|
||||
|
baseConfig(config); |
||||
|
|
||||
|
config.set({ |
||||
|
reporters: ['dots', 'junit'], |
||||
|
|
||||
|
junitReporter: { |
||||
|
outputFile: 'test/results/unit/test-results.xml' |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,18 @@ |
|||||
|
// Executes once before the first describe.
|
||||
|
before(function() { |
||||
|
logLevel = log.getLevel(); |
||||
|
|
||||
|
// Set minimum log level
|
||||
|
log.setLevel(log.levels.SILENT); |
||||
|
}); |
||||
|
|
||||
|
// Executes once after all the describes.
|
||||
|
after(function() { |
||||
|
log.setLevel(logLevel); |
||||
|
}); |
||||
|
|
||||
|
// Executed before every test.
|
||||
|
beforeEach(function() {}); |
||||
|
|
||||
|
// Executed after every test.
|
||||
|
afterEach(function() {}); |
||||
@ -0,0 +1 @@ |
|||||
|
describe('app:main', function() {}); |
||||
@ -0,0 +1,104 @@ |
|||||
|
describe('services:math', function() { |
||||
|
var math = window.math; |
||||
|
|
||||
|
it('should be defined an be an object', function() { |
||||
|
expect(math).not.to.be.undefined; |
||||
|
expect(math).to.be.an('object'); |
||||
|
}); |
||||
|
|
||||
|
it('should have the next methods and properties', function() { |
||||
|
expect(math).to.have.property('total').which.is.a('number'); |
||||
|
expect(math).to.have.property('div').which.is.a('function'); |
||||
|
expect(math).to.have.property('add').which.is.a('function'); |
||||
|
expect(math).to.have.property('addRandom').which.is.a('function'); |
||||
|
}); |
||||
|
|
||||
|
describe('method', function() { |
||||
|
describe('div', function() { |
||||
|
it('should return a number', function() { |
||||
|
expect(math.div(10, 2)).to.be.a('number'); |
||||
|
}); |
||||
|
|
||||
|
it('should return the result of the division', function() { |
||||
|
expect(math.div(10, 2)).to.be.equal(5); |
||||
|
expect(math.div(21, 3)).to.be.equal(7); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an exception if the second parameter is \'0\'', |
||||
|
function() { |
||||
|
expect(function() { |
||||
|
math.div(10, 0); |
||||
|
}).to.throw('divisor-zero'); |
||||
|
} |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
describe('add', function() { |
||||
|
beforeEach(function() { |
||||
|
math.total = 0; |
||||
|
|
||||
|
sinon.stub(window.tools, 'sum') |
||||
|
.withArgs(0, 5).returns(5) |
||||
|
.withArgs(5, 10).returns(15); |
||||
|
}); |
||||
|
|
||||
|
afterEach(function() { |
||||
|
window.tools.sum.restore(); |
||||
|
}); |
||||
|
|
||||
|
it('should return nothing', function() { |
||||
|
expect(math.add(1)).to.be.undefined; |
||||
|
|
||||
|
expect(window.tools.sum).to.have.been.calledOnce; |
||||
|
expect(window.tools.sum).to.have.been.calledWith(0, 1); |
||||
|
}); |
||||
|
|
||||
|
it('should add the current value to the total one', function() { |
||||
|
math.add(5); |
||||
|
|
||||
|
expect(window.tools.sum).to.have.been.calledOnce; |
||||
|
expect(window.tools.sum).to.have.been.calledWith(0, 5); |
||||
|
expect(math.total).to.be.equal(5); |
||||
|
|
||||
|
math.add(10); |
||||
|
|
||||
|
expect(window.tools.sum).to.have.been.calledTwice; |
||||
|
expect(window.tools.sum).to.have.been.calledWith(5, 10); |
||||
|
expect(math.total).to.be.equal(15); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('addRandom', function() { |
||||
|
var randomValue, |
||||
|
defer; |
||||
|
|
||||
|
beforeEach(function() { |
||||
|
math.total = 0; |
||||
|
randomValue = 999; |
||||
|
|
||||
|
defer = new jQuery.Deferred(); |
||||
|
defer.resolve(randomValue); |
||||
|
|
||||
|
sinon.stub(window.tools, 'getRandomNumbers').returns(defer.promise()); |
||||
|
}); |
||||
|
|
||||
|
afterEach(function() { |
||||
|
tools.getRandomNumbers.restore(); |
||||
|
}); |
||||
|
|
||||
|
it('should add the current value to the total one using promise', |
||||
|
function(done) { |
||||
|
math.addRandom().then( |
||||
|
function() { |
||||
|
expect(window.tools.getRandomNumbers).to.have.been.calledOnce; |
||||
|
expect(math.total).to.be.equal(parseInt(randomValue, 10)); |
||||
|
|
||||
|
done(); |
||||
|
}, |
||||
|
done |
||||
|
); |
||||
|
} |
||||
|
); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,76 @@ |
|||||
|
describe('services:tools', function() { |
||||
|
it('should be defined an be an object', function() { |
||||
|
expect(tools).not.to.be.undefined; |
||||
|
expect(tools).to.be.an('object'); |
||||
|
}); |
||||
|
|
||||
|
it('should have the next methods and properties', function() { |
||||
|
expect(tools).to.have.property('sum').which.is.a('function'); |
||||
|
expect(tools).to.have.property('getRandomNumbers').which.is.a('function'); |
||||
|
}); |
||||
|
|
||||
|
describe('method', function() { |
||||
|
describe('sum(*, *)', function() { |
||||
|
it('should return a number', function() { |
||||
|
expect(tools.sum(10, 2)).to.be.a('number'); |
||||
|
}); |
||||
|
|
||||
|
it('should return the addition of integer and float numbers', function() { |
||||
|
expect(tools.sum(10, 2)).to.be.equal(12); |
||||
|
expect(tools.sum(21, 1.9)).to.be.equal(22.9); |
||||
|
}); |
||||
|
|
||||
|
it('should return the addition of numbers in string format', function() { |
||||
|
expect(tools.sum('21', 3)).to.be.equal(24); |
||||
|
expect(tools.sum(.5, '1.2')).to.be.equal(1.7); |
||||
|
}); |
||||
|
|
||||
|
it('should return \'0\' if the parameters are missing', function() { |
||||
|
expect(tools.sum()).to.be.equal(0); |
||||
|
}); |
||||
|
|
||||
|
it('should use \'0\' as default value if the parameter is missing', |
||||
|
function() { |
||||
|
expect(tools.sum(null, 5)).to.be.equal(5); |
||||
|
expect(tools.sum(5)).to.be.equal(5); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
it('should use \'0\' as default value if the parameter is not a number ' + |
||||
|
'nor a string-number', |
||||
|
function() { |
||||
|
expect(tools.sum({}, 5)).to.be.equal(5); |
||||
|
expect(tools.sum(5, function() {})).to.be.equal(5); |
||||
|
} |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
describe('getRandomNumbers', function() { |
||||
|
var randomValue = '999', |
||||
|
server; |
||||
|
|
||||
|
before(function() { |
||||
|
server = sinon.fakeServer.create(); |
||||
|
|
||||
|
server.respondWith(randomValue); |
||||
|
}); |
||||
|
|
||||
|
after(function() { |
||||
|
server.restore(); |
||||
|
}); |
||||
|
|
||||
|
it('should return a random number', function(done) { |
||||
|
tools.getRandomNumbers().then( |
||||
|
function(value) { |
||||
|
expect(value).to.be.equal(parseInt(randomValue)); |
||||
|
|
||||
|
done(); |
||||
|
}, |
||||
|
done |
||||
|
); |
||||
|
|
||||
|
server.respond(); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,3 @@ |
|||||
|
coverage |
||||
|
node_modules |
||||
|
src/main/webapp/lib/**/*.* |
||||
@ -0,0 +1,23 @@ |
|||||
|
A simple and concise demonstration on how to use Mocha, Chai, and Karma to unit and integration test the frontend. |
||||
|
|
||||
|
# Prereqs # |
||||
|
|
||||
|
You must have Node.js installed. |
||||
|
|
||||
|
# Get it running # |
||||
|
|
||||
|
In the root directory, execute: |
||||
|
|
||||
|
```bash |
||||
|
npm install |
||||
|
``` |
||||
|
|
||||
|
To start/run the tests, execute the following in the root directory: |
||||
|
|
||||
|
```bash |
||||
|
node_modules/karma/bin/karma start |
||||
|
``` |
||||
|
|
||||
|
# Watch # |
||||
|
|
||||
|
By default, Karma is configured to watch for changes, so any changes you make to the sample.js or the sampleTest.js will automatically be picked up and tests will be rerun. |
||||
@ -0,0 +1,31 @@ |
|||||
|
var fs = require('fs'), |
||||
|
path = require('path'), |
||||
|
libDirPath, |
||||
|
jqueryFilePath, |
||||
|
jqueryFileName; |
||||
|
|
||||
|
libDirPath = path.join(__dirname, '../src/main/webapp/lib'); |
||||
|
jqueryFileName = 'jquery.min.js'; |
||||
|
jqueryFilePath = path.join(libDirPath, jqueryFileName); |
||||
|
|
||||
|
fs.exists(libDirPath, function(exists) { |
||||
|
if(!exists) { |
||||
|
console.log('Creating lib directory') |
||||
|
fs.mkdir(libDirPath, function(err) { |
||||
|
if(err) { |
||||
|
throw err; |
||||
|
} |
||||
|
|
||||
|
addFiles(); |
||||
|
}); |
||||
|
} else { |
||||
|
addFiles(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
function addFiles() { |
||||
|
console.log('Trying to add files to lib directory!'); |
||||
|
fs.createReadStream(path.join(__dirname, '../node_modules/jquery/dist', jqueryFileName)) |
||||
|
.pipe(fs.createWriteStream(jqueryFilePath)); |
||||
|
console.log('Added files to lib directory.'); |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
var fs = require('fs'), |
||||
|
path = require('path'), |
||||
|
libDirPath, |
||||
|
jqueryFilePath; |
||||
|
|
||||
|
libDirPath = path.join(__dirname, '../src/main/webapp/lib'); |
||||
|
jqueryFilePath = path.join(libDirPath, 'jquery.min.js'); |
||||
|
|
||||
|
fs.exists(jqueryFilePath, function (exists) { |
||||
|
if(exists) { |
||||
|
fs.unlink(jqueryFilePath, function(err) { |
||||
|
if(err) { |
||||
|
throw err; |
||||
|
} |
||||
|
console.log('Successfully deleted jquery.min.js!'); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
@ -0,0 +1,77 @@ |
|||||
|
// Karma configuration
|
||||
|
// Generated on Mon Mar 23 2015 10:59:44 GMT-0700 (PDT)
|
||||
|
|
||||
|
module.exports = function(config) { |
||||
|
config.set({ |
||||
|
|
||||
|
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
|
basePath: '', |
||||
|
|
||||
|
|
||||
|
// frameworks to use
|
||||
|
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
|
frameworks: ['mocha', 'chai', 'sinon'], |
||||
|
|
||||
|
plugins: [ |
||||
|
'karma-mocha', |
||||
|
'karma-coverage', |
||||
|
'karma-chrome-launcher', |
||||
|
'karma-chai', |
||||
|
'karma-sinon' |
||||
|
], |
||||
|
|
||||
|
|
||||
|
// list of files / patterns to load in the browser
|
||||
|
files: [ |
||||
|
'src/main/webapp/app/**/*.js', |
||||
|
'src/main/webapp/lib/**/*.js', |
||||
|
'src/test/**/*.js' |
||||
|
], |
||||
|
|
||||
|
|
||||
|
|
||||
|
// list of files to exclude
|
||||
|
exclude: [], |
||||
|
|
||||
|
|
||||
|
// preprocess matching files before serving them to the browser
|
||||
|
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
|
preprocessors: { |
||||
|
'src/main/webapp/app/**/*.js': ['coverage'] |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// test results reporter to use
|
||||
|
// possible values: 'dots', 'progress'
|
||||
|
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
|
reporters: ['progress','coverage'], |
||||
|
|
||||
|
|
||||
|
// web server port
|
||||
|
port: 9876, |
||||
|
|
||||
|
|
||||
|
// enable / disable colors in the output (reporters and logs)
|
||||
|
colors: true, |
||||
|
|
||||
|
|
||||
|
// level of logging
|
||||
|
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
|
logLevel: config.LOG_INFO, |
||||
|
|
||||
|
|
||||
|
// enable / disable watching file and executing tests whenever any file changes
|
||||
|
autoWatch: true, |
||||
|
|
||||
|
|
||||
|
// start these browsers
|
||||
|
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
|
browsers: ['Chrome'], |
||||
|
|
||||
|
|
||||
|
// Continuous Integration mode
|
||||
|
// if true, Karma captures browsers, runs the tests and exits
|
||||
|
singleRun: false |
||||
|
|
||||
|
}); |
||||
|
}; |
||||
3275
d3-ex3/package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,45 @@ |
|||||
|
{ |
||||
|
"name": "clientside-testing-example", |
||||
|
"version": "0.0.0", |
||||
|
"scripts": { |
||||
|
"preinstall": "node bin/preinstall", |
||||
|
"postinstall": "node bin/postinstall" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"jquery": "1.11.3" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"chai": "^4.2.0", |
||||
|
"coverage": "^0.4.1", |
||||
|
"karma": "^4.4.1", |
||||
|
"karma-chai": "0.1.0", |
||||
|
"karma-chai-sinon": "0.1.5", |
||||
|
"karma-chrome-launcher": "3.1.0", |
||||
|
"karma-coverage": "^2.0.3", |
||||
|
"karma-junit-reporter": "2.0.1", |
||||
|
"karma-mocha": "^2.0.1", |
||||
|
"karma-sinon": "1.0.5", |
||||
|
"mocha": "^8.2.0", |
||||
|
"requirejs": "^2.3.6", |
||||
|
"sinon": "^9.2.0", |
||||
|
"sinon-chai": "^3.5.0" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=0.10.0" |
||||
|
}, |
||||
|
"license": { |
||||
|
"type": "Apache-2.0", |
||||
|
"description": "Apache License Version 2.0, January 2004", |
||||
|
"url": "http://www.apache.org/licenses/LICENSE-2.0" |
||||
|
}, |
||||
|
"keywords": [ |
||||
|
"test", |
||||
|
"testing", |
||||
|
"unit", |
||||
|
"coverage", |
||||
|
"karma", |
||||
|
"mocha", |
||||
|
"chai", |
||||
|
"sinon" |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
{ |
||||
|
"name": "clientside-testing-example", |
||||
|
"version": "0.0.0", |
||||
|
"scripts": { |
||||
|
"preinstall": "node bin/preinstall", |
||||
|
"postinstall": "node bin/postinstall" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"jquery": "*" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"chai": "*", |
||||
|
"karma": "*", |
||||
|
"mocha": "*", |
||||
|
"karma-mocha": "*", |
||||
|
"karma-chai": "*", |
||||
|
"karma-js-coverage": "*", |
||||
|
"karma-sinon": "*", |
||||
|
"karma-chrome-launcher": "*", |
||||
|
"sinon": "*", |
||||
|
"karma-coverage": "*" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=0.12.0" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
|
||||
|
var AWESOME_CLASS = 'awesome', |
||||
|
VIEWPORT_ID = 'viewport', |
||||
|
ERROR_MESSAGE = 'error!', |
||||
|
DATA_SERVICE_URL = '/services/data', |
||||
|
doEverything; |
||||
|
|
||||
|
function makeAwesome(el) { |
||||
|
if(el) { |
||||
|
el.className += ' ' + AWESOME_CLASS; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function isAwesome(el) { |
||||
|
return (el && el.className.indexOf(AWESOME_CLASS) > -1) === true; |
||||
|
} |
||||
|
|
||||
|
function isArray(arr) { |
||||
|
if(Array.isArray(arr)) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
throw new Error(ERROR_MESSAGE); |
||||
|
} |
||||
|
|
||||
|
function getDataFromServer(onSuccess, onError) { |
||||
|
return $.ajax({ |
||||
|
url: DATA_SERVICE_URL |
||||
|
}).error(onError).done(onSuccess); |
||||
|
} |
||||
|
|
||||
|
doEverything = (function() { |
||||
|
var obj = {}; |
||||
|
|
||||
|
privateFunction = function(obj) { |
||||
|
// var obj = {};
|
||||
|
obj.stuff = true |
||||
|
}; |
||||
|
|
||||
|
asyncFunction = function(obj) { |
||||
|
setTimeout(function(){ |
||||
|
obj.async = true; |
||||
|
}, 3000); |
||||
|
}; |
||||
|
|
||||
|
return { |
||||
|
|
||||
|
wow: function() { |
||||
|
console.log('wow'); |
||||
|
}, |
||||
|
|
||||
|
omg: function() { |
||||
|
console.log('omg'); |
||||
|
}, |
||||
|
|
||||
|
addStuff: function (obj) { |
||||
|
if(obj) { |
||||
|
obj.hello = 'hello'; |
||||
|
} |
||||
|
privateFunction(obj); |
||||
|
}, |
||||
|
|
||||
|
asyncStuff: function(obj) { |
||||
|
asyncFunction(obj); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
})(); |
||||
@ -0,0 +1,169 @@ |
|||||
|
describe('sampleTest', function() { |
||||
|
|
||||
|
var div, |
||||
|
clock, |
||||
|
server; |
||||
|
|
||||
|
/** |
||||
|
* Setup function called before each test. |
||||
|
*/ |
||||
|
beforeEach(function() { |
||||
|
div = document.createElement('div'); |
||||
|
|
||||
|
div.id = VIEWPORT_ID; |
||||
|
document.body.appendChild(div); |
||||
|
|
||||
|
// Fake the clock before every test
|
||||
|
clock = sinon.useFakeTimers(); |
||||
|
// Creates a new fake server before every test
|
||||
|
server = sinon.fakeServer.create(); |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* Tear down function called after each test. |
||||
|
*/ |
||||
|
afterEach(function() { |
||||
|
document.body.removeChild(div); |
||||
|
|
||||
|
// Tears down the faked clock after every test
|
||||
|
clock.restore(); |
||||
|
// Tears down the fake server after every test
|
||||
|
server.restore(); |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* Demonstrates the expectation that the div added to the body contains |
||||
|
* the AWESOME_CLASS after calling #makeAwesome(). |
||||
|
*/ |
||||
|
it('#makeAwesome(): should add class AWESOME_CLASS to a div', function() { |
||||
|
expect(div.className).to.not.contain(AWESOME_CLASS); |
||||
|
|
||||
|
makeAwesome(div); |
||||
|
|
||||
|
expect(div.className).to.contain(AWESOME_CLASS); |
||||
|
}); |
||||
|
|
||||
|
it('#isAwesome(): should return true when el contains class AWESOME_CLASS', function() { |
||||
|
expect(isAwesome(div)).to.be.false; |
||||
|
|
||||
|
makeAwesome(div); |
||||
|
|
||||
|
expect(isAwesome(div)).to.be.true; |
||||
|
}); |
||||
|
|
||||
|
it('#isAwesome(): should return false when el is null', function() { |
||||
|
expect(isAwesome(null)).to.be.false; |
||||
|
}); |
||||
|
|
||||
|
it('#isAwesome(): should return false when el doesn\'t contain class "awesome"', function() { |
||||
|
expect(isAwesome(document.createElement('div'))).to.be.false; |
||||
|
}); |
||||
|
|
||||
|
it('#isArray(): should return true when arr is an Array', function() { |
||||
|
expect(isArray([])).to.be.true; |
||||
|
}); |
||||
|
|
||||
|
it('#isArray(): should throw an exception when arr is not an Array', function() { |
||||
|
assert.throws(function() { |
||||
|
isArray(null); |
||||
|
}, ERROR_MESSAGE); |
||||
|
}); |
||||
|
|
||||
|
it('doEverything#addStuff(): should add keys "hello" and "stuff" to obj', function() { |
||||
|
var obj = {}; |
||||
|
|
||||
|
doEverything.addStuff(obj) |
||||
|
|
||||
|
expect(obj).to.include.keys(['hello', 'stuff']); |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* Demonstrates that the #asyncStuff() function sets the "async" property |
||||
|
* on the parameter obj once the timeout has lapsed. |
||||
|
*/ |
||||
|
it('doEverything#asyncStuff(): should set obj.async to "true"', function() { |
||||
|
var obj = {}; |
||||
|
|
||||
|
doEverything.asyncStuff(obj); |
||||
|
// Fast-forward 4 seconds to trip the setTimeout() in myAsnycFn()
|
||||
|
clock.tick(4000); |
||||
|
|
||||
|
expect(obj).to.include.keys(['async']); |
||||
|
expect(obj.async).to.be.true; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* Demonstrates the #getDataFromServer() function properly calls the success function |
||||
|
* passed to #getDataFromServer() when the server returns a HTTP 200. |
||||
|
*/ |
||||
|
it('#getDataFromServer(): should get object with "name" property from my service when a HTTP 200 is returned', function() { |
||||
|
var serverResponse, |
||||
|
ajaxResponseData; |
||||
|
|
||||
|
serverResponse = { |
||||
|
name: 'Jim Smith' |
||||
|
}; |
||||
|
|
||||
|
// Configure the fake server to respond with the configuration
|
||||
|
server.respondWith('GET', |
||||
|
DATA_SERVICE_URL, [ |
||||
|
200, { |
||||
|
'Content-Type': 'application/json', |
||||
|
}, |
||||
|
JSON.stringify(serverResponse) |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
getDataFromServer(function(data) { |
||||
|
ajaxResponseData = data; |
||||
|
}); |
||||
|
// Tell the server to respond with the GET 200 response
|
||||
|
server.respond(); |
||||
|
|
||||
|
expect(ajaxResponseData).to.include.keys(['name']); |
||||
|
expect(ajaxResponseData.name).to.equal(serverResponse.name); |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* Demonstrates the #getDataFromServer() function properly calls the error function |
||||
|
* passed to #getDataFromServer() when the server returns a HTTP 500. |
||||
|
*/ |
||||
|
it('#getDataFromServer(): should get an error object from my service when an HTTP 500 is returned', function() { |
||||
|
var serverErrorResponse, |
||||
|
ajaxResponseData, |
||||
|
ajaxErrorData, |
||||
|
onError, |
||||
|
onSuccess; |
||||
|
|
||||
|
serverErrorResponse = { |
||||
|
error: 'Internal error!' |
||||
|
}; |
||||
|
|
||||
|
// Configure the fake server to respond with the configuration
|
||||
|
server.respondWith('GET', |
||||
|
DATA_SERVICE_URL, [ |
||||
|
500, { |
||||
|
'Content-Type': 'application/json', |
||||
|
}, |
||||
|
JSON.stringify(serverErrorResponse) |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
onSuccess = function(data) { |
||||
|
ajaxResponseData = data; |
||||
|
}; |
||||
|
onError = function(jqXhr, errorString, exception) { |
||||
|
ajaxErrorData = jqXhr.responseJSON.error; |
||||
|
}; |
||||
|
|
||||
|
getDataFromServer(onSuccess, onError); |
||||
|
// Tell the server to respond with the GET 500 response
|
||||
|
server.respond(); |
||||
|
|
||||
|
// AJAX response failed, the response data should be empty
|
||||
|
expect(ajaxResponseData).to.be.not.ok; |
||||
|
// Make sure the AJAX response returned the server error
|
||||
|
expect(ajaxErrorData).to.equal(serverErrorResponse.error); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue