Browse Source

pushing again

master
Ganesh 5 years ago
parent
commit
1c08f2cdda
45 changed files with 33657 additions and 0 deletions
  1. 21
      d3-ex1/LICENSE.txt
  2. 11
      d3-ex1/README.md
  3. 73
      d3-ex1/karma.conf.js
  4. 10253
      d3-ex1/lib/jquery-3.2.1.js
  5. 270
      d3-ex1/mocha.css
  6. 1571
      d3-ex1/package-lock.json
  7. 26
      d3-ex1/package.json
  8. 5332
      d3-ex1/test-lib/chai.js
  9. 6472
      d3-ex1/test-lib/mocha.js
  10. 30
      d3-ex1/test.html
  11. 109
      d3-ex1/tests.js
  12. 54
      d3-ex1/utils.js
  13. 3
      d3-ex2/.bowerrc
  14. 43
      d3-ex2/.gitignore
  15. 28
      d3-ex2/Chrome_86.0.4240_(Linux_0.0.0)/test/results/unit/test-results.xml
  16. 33
      d3-ex2/Gruntfile.js
  17. 202
      d3-ex2/LICENSE
  18. 88
      d3-ex2/README.md
  19. 24
      d3-ex2/app/index.html
  20. 43
      d3-ex2/app/scripts/controllers/math.js
  21. 5
      d3-ex2/app/scripts/main.js
  22. 52
      d3-ex2/app/scripts/services/math.js
  23. 38
      d3-ex2/app/scripts/services/tools.js
  24. 20
      d3-ex2/bower.json
  25. 21
      d3-ex2/build/grunt-tasks/connect.js
  26. 30
      d3-ex2/build/grunt-tasks/test.js
  27. 4703
      d3-ex2/package-lock.json
  28. 60
      d3-ex2/package.json
  29. 64
      d3-ex2/test/config/karma.conf.js
  30. 29
      d3-ex2/test/config/karma.coverage.conf.js
  31. 15
      d3-ex2/test/config/karma.unit.conf.js
  32. 18
      d3-ex2/test/config/mocha-globals.js
  33. 1
      d3-ex2/test/specs/unit/main.js
  34. 104
      d3-ex2/test/specs/unit/services/math.spec.js
  35. 76
      d3-ex2/test/specs/unit/services/tools.spec.js
  36. 3
      d3-ex3/.gitignore
  37. 23
      d3-ex3/README.md
  38. 31
      d3-ex3/bin/postinstall.js
  39. 18
      d3-ex3/bin/preinstall.js
  40. 77
      d3-ex3/karma.conf.js
  41. 3275
      d3-ex3/package-lock.json
  42. 45
      d3-ex3/package.json
  43. 26
      d3-ex3/packagejson.old
  44. 68
      d3-ex3/src/main/webapp/app/sample.js
  45. 169
      d3-ex3/src/test/sampleTest.js

21
d3-ex1/LICENSE.txt

@ -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.

11
d3-ex1/README.md

@ -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.

73
d3-ex1/karma.conf.js

@ -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

270
d3-ex1/mocha.css

@ -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

26
d3-ex1/package.json

@ -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

6472
d3-ex1/test-lib/mocha.js
File diff suppressed because it is too large
View File

30
d3-ex1/test.html

@ -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>

109
d3-ex1/tests.js

@ -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);
});
});

54
d3-ex1/utils.js

@ -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);

3
d3-ex2/.bowerrc

@ -0,0 +1,3 @@
{
"directory": "app/bower-components"
}

43
d3-ex2/.gitignore

@ -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

28
d3-ex2/Chrome_86.0.4240_(Linux_0.0.0)/test/results/unit/test-results.xml

@ -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>

33
d3-ex2/Gruntfile.js

@ -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']);
};

202
d3-ex2/LICENSE

@ -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.

88
d3-ex2/README.md

@ -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 | |
----------------------|----------|----------|----------|----------|----------------|
```

24
d3-ex2/app/index.html

@ -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>

43
d3-ex2/app/scripts/controllers/math.js

@ -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));

5
d3-ex2/app/scripts/main.js

@ -0,0 +1,5 @@
(function(log) {
var logger = log.getLogger('main');
logger.info('Application started!');
}(window.log));

52
d3-ex2/app/scripts/services/math.js

@ -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));

38
d3-ex2/app/scripts/services/tools.js

@ -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));

20
d3-ex2/bower.json

@ -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"
}
}

21
d3-ex2/build/grunt-tasks/connect.js

@ -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
}
}
});
};

30
d3-ex2/build/grunt-tasks/test.js

@ -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

60
d3-ex2/package.json

@ -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"
}
}

64
d3-ex2/test/config/karma.conf.js

@ -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
});
};

29
d3-ex2/test/config/karma.coverage.conf.js

@ -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];
}
}
]
}
});
};

15
d3-ex2/test/config/karma.unit.conf.js

@ -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'
}
});
};

18
d3-ex2/test/config/mocha-globals.js

@ -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() {});

1
d3-ex2/test/specs/unit/main.js

@ -0,0 +1 @@
describe('app:main', function() {});

104
d3-ex2/test/specs/unit/services/math.spec.js

@ -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
);
}
);
});
});
});

76
d3-ex2/test/specs/unit/services/tools.spec.js

@ -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();
});
});
});
});

3
d3-ex3/.gitignore

@ -0,0 +1,3 @@
coverage
node_modules
src/main/webapp/lib/**/*.*

23
d3-ex3/README.md

@ -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.

31
d3-ex3/bin/postinstall.js

@ -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.');
}

18
d3-ex3/bin/preinstall.js

@ -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!');
});
}
});

77
d3-ex3/karma.conf.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

45
d3-ex3/package.json

@ -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"
]
}

26
d3-ex3/packagejson.old

@ -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"
}
}

68
d3-ex3/src/main/webapp/app/sample.js

@ -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);
}
};
})();

169
d3-ex3/src/test/sampleTest.js

@ -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);
});
});
Loading…
Cancel
Save