Nodebots Javascript and Robotic in The Real World
Nodebots Javascript and Robotic in The Real World
world
Wilson Mendes
This book is for sale at http://leanpub.com/nodebots-javascript-and-robotic-in-the-real-world
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Appendix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Breadboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Piezo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Resistors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
LED (Light-emitting diode) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Sensors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Protective Conductor (Ground or Ground Wire) . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Jumper Wires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Push Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Next steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
About the author
GDE (Google Developer Expert) AngularJS, GDG Salvador organiser, passionate about technology
and active in communities focused on web development, including AngularJS, JavaScript, HTML5,
CSS3, workflow, web performance, security and Internet of things. Participates in the organisation
of events, lectures at conferences in Brazil and in other countries and contributes to few open source
projects.
Introduction
This book is for anyone who wants to take the first steps on Nodebots or has an interest in deliving
into some concepts that are poorly demonstrated on the subject.
It will be shown contents with simple and low-cost sensors, however relating the sensors with
real integrations of a nodebots application, such as integration between external APIs from events
reading some sensor data, among others.
In addition, the book is covering important topics in the test questions, showing good practices for
testing coverage and how to test with quality and performance, with integrations in external services
for automation of code quality and build validation tasks.
Another point is the evolution of the application with a focus on the architecture itself. How to use
software architecture standards in any type of project and how to take advantage of each of them
applying to your system.
Acknowledgements
I would like to say thank initially my mother (who has played the role of mother and father for
many times), this wonderful person who teaches me many of the values that I take with me until
today, being my example to this day and my brother Leonardo Araújo who was always with me.
I would like to thank my wife, Nilana Rocha, for all her support. It was one of the crucial factors
and without a doubt, this book would not be a reality. Thank you so much for everything.
A great time to remember all my friends from Lingu�gil Group and #horaExtraSSA from
Salvador-BA. You were people who motivated me to be the professional that I am today, encouraging
and motivating me to seek and share knowledge. Thank you all. For some Recife’s folks who are
amazing people and presented me with cheese curd, macaxeira (or aipim?) and much love.
Finally, I would like to thank Nelson Glauber¹ and Fagner Brack² for their support in reviewing
the contents of this book and for everyone who Have contributed directly or indirectly to the
composition of this book, such as Rafael Gomes³, Juliano Bersano⁴, Sarah Atkinson⁵ and several
others who supported me in this process and suggested improvements at this stage.
Thank you all.
Wilson Mendes
¹https://twitter.com/nglauber
²https://twitter.com/fagnerbrack
³https://twitter.com/gomex
⁴https://twitter.com/julianobersano
⁵https://twitter.com/SarahvAko
Nodebots and microcontrollers
What are nodebots?
NodeBots is a term used to define the concept of control over open hardware, an electronic hardware
designed and offered in the same manner and with the same licenses as free code software, sensors
and other electronic components using NodeJS. And you can use several elements: from sensors,
servo motors⁶, wheels, motion detectors, cameras, LED displays, audio players and more.
In some moments the concept of Nodebots is directly connected to the concept of IoT - Internet of
Things. Internet of Things * is a technological revolution in order to connect everyday electronic
devices such as home appliances and even industrial machines and devices with internet access and
other technical innovations in important fields such as home automation from Sensors.
The whole idea of NodeBots evolved according to increasing capabilities in NodeJS and the effort
of some developers like Nikolai Onken, J��rn Zaefferer, Chris Williams, Julian Gautier and Rick
Waldron who worked to develop the various modules we use in NodeBots applications nowadays.
The node-serialport⁷ module, created by Chris Williams, was the kick-start because it allows access
to devices using low-level serial ports.
Julian Gautier then implemented Firmata⁸, a protocol used to access microcontrollers, such as
Arduinos, via code using JavaScript for communication between physical components.
Rick Waldron went a little further. Using the Firmata library as a base, he created a framework to
assist in building Nodebots and Internet stuff called Johnny-Five.
The Johnny-Five framework makes it easy to control various components, from LEDs to various
other types of sensors in a simple and practical way. This is what many NodeBots now use to achieve
some awesome feats!
Microcontrollers
When we talk about nodebots, we are indirectly mentioning microcontrollers. A microcontroller is
a smaller and simpler computer that has a simple programmable physical circuit board (we’ll call it
pins, inputs, etc.) that can detect multiple inputs and outputs.
An Arduino is one of the several types of microcontrollers, being one of the most common for
experiments and validations between software and hardware integration. There are other types of
microcontrollers too, which can be powered by Node including:
⁶https://pt.wikipedia.org/wiki/Servomotor
⁷https://www.npmjs.com/package/serialport
⁸https://www.npmjs.com/package/firmata
Nodebots and microcontrollers 5
• Raspberry Pi⁹;
• Tessel¹⁰;
• Espruino¹¹;
• BeagleBone¹²;
In this book, I will use Arduino UNO¹³ in the examples, but feel free to use the microcontroller of
your choice.
NodeJS
NodeJS is a JavaScript execution runtime built on the Javascript engine Javascript V8, enabling the
use of Javascript in other environments beyond the web and with an important aspect of non-
blocking input model usage and Event-driven data output. It aims to help programmers to create
high-scalability applications such as web servers with concurrent connections, asynchronous scripts
and even integration with electronic components, that is our case.
It was created by Ryan Dahl in 2009, and its development is maintained by the community and the
Node Foundation, of which companies such as IBM, Google, Red Hat, Joyent, among others.
Installing on Windows
Installing NodeJS on Windows is quite simple. One way is to visit the official project website¹⁴ and
download the installer in the format, click on the installation options and finish the installation.
When finished open your command prompt by accessing the Windows command prompt from the
“Run> cmd” command and, after starting the program, enter the following command:
1 $ node -v
It should display at the prompt the current version of NodeJS on your terminal. This completes the
installation in the Windows operational system environment.
Wget:
Packages like CURL and WGET may not be installed on your operating system by
default. If necessary, check the best method for your operating system or access the NVM
repository on Github to check the installation steps or possible problem solutions.
Next, you should open your file that stores the default configuration of your terminal, which can be
located in ∼/.bash_profile, ∼/.zshrc, ∼/.profile or ∼/.bashrc, and add these lines at the end
of this configuration file so that you will load NVM next time you access the command line.
1 export NVM_DIR="$HOME/.nvm"
2 [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
With this, as soon as you recharge your terminal the NVM will be available. Now just install the
version of NodeJS of your choice. In this book, we will use version 12.16.2.
After these commands NVM will download the specific version of NodeJS, making it accessible via
the terminal. To verify that the command completed successfully, enter the command:
1 $ node -v
The result should be v12.16.2. If this was the return of your command, you’re all set for the next
steps. If you have any problems make sure that the NVM loading code has been inserted into the
configuration file of your terminal and start another instance of your terminal.
A file with the commands in this topic was created for the installation of NVM and Node with the
version used in this book. If you want to use it, please download nvm-install.sh¹⁶ file in this gist link.
¹⁶https://gist.github.com/willmendesneto/4c951413bacbb8850837a53bcdada30d
Nodebots and microcontrollers 7
To continue you will need to answer some basic questions about the project, such as: - Package
name; - Project version; - Description of the project; - Name of the project’s main file. This will
be produced at the end of your project, after all minification, obfuscation and other optimisation
procedures of your javascript code;
You can rest assured that none of them is mandatory. If you do not know or do not want to respond
now, just hit the “Enter” key until the end or run this command with -y flag, that means you are
answering yes for all those questions. It will then create a package.json file with this information
from your repository.
¹⁷https://docs.npmjs.com/cli/init
Nodebots and microcontrollers 8
Command output
Adding Packages
Now that we have our package.json¹⁸ with all the basic configurations of our repository, we will
install our first package to integrate with our project!
NPM functions are the official package manager for NodeJS applications, being the largest ecosystem
of libraries and open source modules in the world.
With it you can add packages in your application from the command npm install¹⁹, informing the
name of the package published in the official site of npm and the format that we want to save the
Package in the application. We have some options for adding packages, which are:
• locally as a development dependency: the package will be installed locally and accessible in
the node_modules folder and the package information will be saved in the devDependencies
key of your JSON file. To use this option add the flag --save-dev;
• locally as a dependency of your project: the package will be installed locally and accessible in
the node_modules folder and the package information will be saved in the dependencies key
of your JSON file. To use this option add the flag --save;
• globally: the package will be installed with global scope and accessible in any other project. To
use this option add the flag --global;
¹⁸https://docs.npmjs.com/files/package.json
¹⁹https://docs.npmjs.com/cli/install
Nodebots and microcontrollers 9
Packages saved as a development dependency and globally won’t be used when you
publish your project, so use these options carefully.
For our initial project we will install the Johnny-Five framework as one of the development
dependencies by running the command:
You may notice that we now have some new files in our project folder. Firstly, the folder node_-
modules was created and inside it, we have our package installed successfully.
Another new content information were added in the package in the dependencies block of our
package.json file.
1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "test": "echo \"Error: no test specified\" && exit 1"
8 },
9 "author": "",
10 "license": "ISC",
11 "dependencies": {
12 "johnny-five": "^1.4.0"
13 }
14 }
Nodebots and microcontrollers 10
NPM has other standard commands and we can use them in our application. If you would
like to know more about these commands, go to the page about these commands in the
official NPM documentation²⁰.
1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "dev": "echo \"This is our 'dev' NPM task\"",
8 "test": "echo \"Error: no test specified\" && exit 1"
9 },
10 "author": "",
11 "license": "ISC",
12 "dependencies": {
13 "johnny-five": "^1.4.0"
14 }
15 }
Notice that in line 7 we add our new NPM command through the JSON scripts field. Notice that we
already had the “test” command which is a standard npm command and it’s in the same area as our
new command, as this is the default area for adding commands to be executed by NPM.
To run the command, just access the terminal or command prompt and type npm run dev. The result
will return as follows:
²⁰https://docs.npmjs.com/misc/scripts
Nodebots and microcontrollers 11
Arduino
Arduino … ardu-WHAT?
Arduino is an open-source platform based on easy-to-use and plug-and-play hardware and integra-
tion with sensors from the software. Being a fully malleable and open platform anyone can use it in
projects of the most diverse as simple data checks received by light sensors, temperature, humidity,
home automation and more.
Some advantages are:
• Cost: the value of an Arduino is very low. Currently, the cost of an Arduino UNO²¹ is something
around $ 50.00 and Arduino Nano²² costs between $ 15.00 and $ 20.00, being more or less
according to the model of your choice;
• Cross-platform: Arduino is compatible with all operating systems and platforms;
• Simple: It does not require of those who will manipulate it a vast knowledge in electronics. Just
have a basic notion of development and you can already do things pretty cool;
There is a page on the Arduino Project Wiki with solutions to the most common problems²⁴, if you
have any kind of inconvenience with the installation and first setup Of the Arduino IDE.
After the installation of the Arduino IDE, we will now access the program and verify its operation.
Firstly we realize that the Arduino IDE has some examples integrated as a mediator and facilitator
²³https://www.arduino.cc/en/Main/Software
²⁴https://github.com/arduino/Arduino/wiki/Building-Arduino
²⁵https://github.com/johnny-five-io/nodebots-interchange
Nodebots and microcontrollers 13
for those who have never had contact with the platform. To check the complete list of examples, just
access "File> Examples".
Note that in the example codes are written in [C language](https://pt.wikipedia.org/wiki/C_(pro-
gram language% C3% A7% C3% A3o) and not in Javascript, but nothing prevents you from running
the example code based in other programming languages.
Let’s now connect our Arduino board into our operating system. Arduino IDE has already stored
the configuration of my Arduino, which appears in the item “Board: Arduino Genuino/UNO”, but
in the first time will appear in the “Port” option, which has the complete listing of serial ports based
Nodebots and microcontrollers 14
The name will appear with the prefix “/dev/cu.” and will have the name of the Arduino, facilitating
the integration. Choose the port which your Arduino is connected and that’s all: the connection was
successful.
Firmata
Firmata is a protocol for communicating with microcontrollers software on a computer (or
smartphone/tablet, etc). The protocol can be implemented in the firmware of any microcontroller
architecture, such as any computer software package.
Our next step after installing the Arduino IDE is to add the Firmata protocol in our Arduino. Let’s
open our Arduino IDE and access the "Files> Examples> Firmware> StandardFirmata" option.
Nodebots and microcontrollers 15
With the Arduino plugged into your computer you can run the following code and wait for the IDE
shows the message that everything happened successfully.
Nodebots and microcontrollers 16
Johnny Five
Johnny-Five is an open source framework that allows you to control micro-controllers and compo-
nents using very similar functions that would be used if you were programming only for the Arduino
platform itself, but using JavaScript and implementing Firmata protocol for communication with
software and computer host.
This allows you to write a custom firmware without having to create your own protocol and objects
for the programming environment you are using. In summary, Johnny-Five is a node package that
allows you to program micro controllers using JavaScript!
Nodebots and microcontrollers 17
After this command, NPM will create the folder node_modules and within it, we will have our
johnny-five package installed and accessible in the context of our project.
We can also check that our package.json has changed. In it were added the information of the name
of our NodeJS package and the installed version, as we can see in the code below.
1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "test": "echo \"Error: no test specified\" && exit 1"
8 },
9 "author": "",
10 "license": "ISC",
11 "dependencies": {
12 "johnny-five": "^1.4.0"
13 }
14 }
1 ...
2 const five = require('johnny-five');
3 ...
This command makes the request of the package, making it accessible to our project. Now we’ll meet
our first class Jonny Five: the Board.
The Board class returns to the application an object that represents the physical board itself in the
electronics. All device objects depend on this object to be initialized.
Nodebots and microcontrollers 18
1 ...
2 const board = new five.Board();
3 ...
The board object has a .on() method, which is commonly used in Javascript applications for creating
event handlers. This method accepts 2 parameters:
In this example, we will call this method with the ready option, which verifies when the code is
already accessing the physical card used.
1 ...
2 board.on('ready', () => {
3 console.log('Hello World!');
4 });
5 ...
And the final content of our index.js has been pretty tight, as you can see below.
Now we can run our code via the command line by typing the command:
1 $ node index.js
And the result returned will be the message “Hello World”, as you can see in the figure below.
Nodebots and microcontrollers 19
If you want to make it easier, we can use npm start, one of the standard NPM commands we can
create in our package.json, making it accessible via the command line.
1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "start": "node index.js",
8 "dev": "echo \"This is our 'dev' NPM task\"",
9 "test": "echo \"Error: no test specified\" && exit 1"
10 },
11 "author": "",
12 "license": "ISC",
13 "dependencies": {
14 "johnny-five": "^1.4.0"
15 }
16 }
After adding the command, just type in the npm start command line and the result will be the same
message as the previous command.
In this topic, you have seen the integration between our Javascript code and the hardware. In the next
chapters, we will see more examples showing in a simple and fun way how to integrate Nodebots
into our daily life.
First project: Build Checker
What is a pipeline build
In some presentations, I realised that this term is not known for many people, even those who are
more familiar with approaches such as continuous integration and continuous delivery.
Build pipeline is a concept that was built in the middle of 2005 and it’s based on the idea of task
parallelization, separating each step into small acceptance criteria for the application. It is worth
remembering that these steps can be automatic or manual.
Materials needed
For this project we will use:
²⁶https://hubot.github.com/
²⁷https://github.com/codedance/Retaliation
²⁸http://ccmenu.org/
First project: Build Checker 21
• 1 Protoboard: A protoboard is nothing more than a plate with holes and conductive connec-
tions for mounting experimental electrical circuits, without the need for welding. A simple
protoboard costs between $ 5.00 to $ 10.00 and can be found in any electric store;
• 2 LEDs (light-emitting diode): 1 red to signal the broken build and 1 green to signal that the
build was successfully completed. A LED costs less than $ 0.50 and can be found at any electrical
store;
• Arduino with 2 GND (ground) ports;
GND ports are called “ground” ports. They are electrical conductors that connect to the Earth - that
is, to the Electric Earth. Since it is always neutral and (theoretically) present in every electric circuit,
it is always taken as a reference point for the measurement of potentials, containing zero volts.
Now just plug the 2 LEDS, linking as follows:
The following image illustrates the assembly of the components with the Arduino.
First project: Build Checker 22
1 $ mkdir build-checker
2 $ cd build-checker
We will start our application by typing the npm init command with the -y flag which means that all
the answers that were previously made will be answered and registered with the default response.
After this, we will install the johnny-five package locally as a dependency in the folder of our project.
First project: Build Checker 23
1 $ npm init -y
2 $ npm install --save johnny-five
Inside the folder of our project we will create the folder src and inside it, our file index.js where
will be our content.
1 $ mkdir src
2 $ touch src/index.js
In the index.js file we will import the Johnny-five package using the require command and create
our protoboard instance to add the LED.
1 ...
2 const five = require('johnny-five');
3 const board = new five.Board();
4 ...
For the first activity with the component, we will use a new Johnny Five class called LED. To use this
class we need to pass the value of the pin that the LED is connected to the Arduino.
1 ...
2 const led = new five.Led(12);
3 ...
Now let’s put our code to work on our Arduino. Inside the folder of our project, we will enter in our
command prompt/terminal the following command:
First project: Build Checker 24
1 $ node src/index.js
After this command, our prompt/command line is showing that the command has run successfully
and the result will be that our two LEDs will be flashing.
Quite simple, is not it? In the next topic, we will think a little more about our architecture.
When registering the project it will appear in the upper part, on the right, a field with the name
“CCTray” that, when we click, it directs to the XML file with the information of the build.
²⁹https://snap-ci.com
³⁰http://www.martinfowler.com/articles/continuousIntegration.html
First project: Build Checker 25
And these are the information that we will consult with our build-checker.
1 <Projects>
2 <Project
3 name="brasil-de-fato/news-service (master) :: CONTRACT-TEST"
4 activity="Sleeping"
5 lastBuildLabel="77"
6 lastBuildStatus="Success"
7 lastBuildTime="2016-01-21T18:46:48Z"
8 webUrl="https://snap-ci.com/brasil-de-fato/news-service/branch/master/logs/defaultPi\
9 peline/77/CONTRACT-TEST"/>
10 </Projects>
Analysing the information we realise that the only information we should validate for our build
is the data from the lastBuildStatus field. It is returned if the build was successfully completed,
successfully completed, or is happening at the time of validation.
The lastBuildStatus field can contain:
• Success: the last job on the server has been successfully completed;
• Failure: the last job on the server was terminated with error;
• Pending: the task is happening right now on the server;
• Exception and Unknown: Something unexpected occurred with the current task on the server.
The reasons are the most diverse, as the server had a swing in the middle of the task or he
never ran a certain task yet, so he does not have the information;
Let’s now add our URL containing the CCTray information from our project and create it with the
request for this information. For this, we will add the package NodeJS request³¹, an HTTP client
that was developed in order to facilitate the creation of HTTP or HTTPS requests. To add the new
packages we will enter the following command.
³¹https://github.com/request/request
First project: Build Checker 26
The use of the request is very simple, accepting 2 parameters being the first the URL and
the second the function that will manipulate the requested information. For more details,
see full documentation of the request package in Github³².
Now let’s add the package in our project and create the request using the RequestJS module.
1 ...
2 const request = require('request');
3 const five = require('johnny-five');
4 const board = new five.Board();
5 ...
And we’ll add the CCTray address that we’ve copied from our continuous integration server into
our code and create the HTTP request to read the information.
• error: an object with the error information that happened. If the request does not return any
error it has the default value null;
• response: object with the request response information;
• body: String with the information of the body of the return of the requisition;
In our code then we will analyse the return of the requisition and create the appropriate treatments.
The first treatment will be the verification of the error and in case we have an error we will treat
the error.
³²https://github.com/request/request#table-of-contents
First project: Build Checker 27
1 ...
2 request(CI_CCTRACKER_URL, function(error, response, body) {
3 if (error) {
4 console.log('Something is wrong in our CI/CD =(');
5 return;
6 }
7 ...
8 });
If the response does not return any error, we will treat the message to turn the LEDs on and off for
visual feedback.
1 ...
2 if(body.indexOf('Failure') !== -1) {
3 console.log('Your CI/CD is broken! Fix it!!!!');
4 ledSuccess.off();
5 ledError.on();
6 } else {
7 console.log('Your CI/CD is ok!');
8 ledSuccess.on();
9 ledError.off();
10 }
11 ...
With the request created, we will only create a gap between each request, using a simple
setInterval. We will use a time of 500 milliseconds for code validation, but this value can be
changed to whatever is ideal for you.
1 setInterval(() => {
2 request(CI_CCTRACKER_URL, function(error, response, body) {
3 ..
4 });
5 }, 500);
It will query the data at a preconfigured interval and check the current state of the build, based on
First project: Build Checker 29
information from all pipelines. If there is no word “Failure” in the response to the request, something
wrong has happened and our build checker will turn on the red light, otherwise, the green light will
remain on, signalling that everything is ok.
1 // src/configuration.js
2 module.exports = {
3 LED: {
4 SUCCESS: 12,
5 ERROR: 10
6 },
7 CI_CCTRACKER_URL: 'https://snap-ci.com/willmendesneto/generator-reactor/branch/mas\
8 ter/cctray.xml',
9 INTERVAL: 1000
10 };
Now let’s change our src/index.js to use our file with the default settings of our application.
Our code is starting to get a little more expressive, do you agree? But is there still something we can
improve on? Of course yes!
We are talking about build checker, but we have no abstraction for this operation. The idea is that our
final code is just an initialization of our app, with all relevant information within this abstraction.
Let’s then create our file with the LED information abstractions and the HTTP request by accessing
the settings.
36 }, CONFIG.INTERVAL);
37 };
38
39 module.exports = BuildChecker;
And our src/index.js file will only invoke and start our code so that the LEDs start blinking.
Finishing this separation of concepts, we improve the readability, maintainability and several other
variants of our application. It is worth mentioning that this is a good practice and that, in the course
of the book, we will always be thinking about improvements to our final code.
Pyramid of tests
We’ll talk only about unit tests, if you’d like to know more about all layers, read the post
“TestPyramid”³⁴ by Martin Fowler.
The idea of the unit test is to validate and certify that your code is doing what it intends to do, giving
feedback on the errors quickly before we deploy our project to production.
One aspect that nobody explains very well is about the tests in Nodebots, which has as the main
objective, in this case, to create the electrical simulations and with mocks and stubs thus simulating
the communication between components.
Let’s now create a folder for our unit tests with the name test.
1 $ mkdir test
The unit tests will use the test framework MochaJS³⁵, SinonJS³⁶ for spies, stubs and mocks and
ShouldJS³⁷ for assertions. Let’s then install these packages as a dependency of project development.
³⁴http://martinfowler.com/bliki/TestPyramid.html
³⁵https://mochajs.org
³⁶http://sinonjs.org
³⁷https://shouldjs.github.io
First project: Build Checker 33
A fundamental NodeJS package in this step is mock-Firmata³⁸, created by Rick Waldron to make
testing setup in Johnny-Five applications easier. The integration is very simple, you just need to
load and create your fake component of the board in the test, as we can see in our setup file of
test/spec-helper.js tests.
1 require('should');
2 const mockFirmata = require('mock-firmata');
3 const five = require('johnny-five');
4
5 const board = new five.Board({
6 io: new mockFirmata.Firmata(),
7 debug: false,
8 repl: false
9 });
Let’s then add a simple test to check the integration of our tests. First, we will create a file with some
MochaJS settings inside the test folder. This will be the initial content of our mocha.opts.
1 --reporter spec
2 --recursive
3 --require test/spec-helper.js
4 --slow 1000
5 --timeout 5000
³⁸https://github.com/rwaldron/mock-firmata
First project: Build Checker 34
Our tests use the describe and it scope methods. Describe is used to group scenarios, in our
case Test validation. It is the identification of one of the test points. Note also that we have the
should.be.equal method that will compare if the first value is equal to the second.
1 $ ./node_modules/bin/mocha
We will then see this information at our prompt/terminal and our initial setup was a success!
Now, let’s create the scenarios for our tests. Let us then define the scenarios that we must cover in
our tests:
One way to validate when the build checker should flash the LED is to create a stub for the request
using the node-request to validate the response by expected types (success and error) and use some
spies for the LEDs.
First project: Build Checker 35
For this simulation, we will create some fixtures with the server responses model for success and
error. Let’s then create our folder with the data inside our folder containing our tests.
1 $ mkdir test/fixtures
2 $ touch test/fixtures/success.xml test/fixtures/error.xml
Let’s then create the scenario to validate our code. Some things we should keep in mind about our
unit testing framework is that its beforeEach method, which happens before every it method. We
will use as documentation of each step that must occur to reproduce the specific scenario.
Let’s then explain more about the contents of this file and why of each test. We create the tests of
the instance of our BuildChecker and its initial attributes.
First project: Build Checker 36
Now we will validate when we stop our polling. Let’s now use the spy method of the sinon to check
if the code used the clearInterval method to end with the requests. For this, we will check if the
global.clearInterval was used once, by accessing the boolean calledOnce, which is an internal
counter added by the sinon.spy method for the tests.
1 ...
2 describe('#stopPolling', () => {
3 beforeEach(() => {
4 sinon.spy(global, 'clearInterval');
5 buildChecker.stopPolling();
6 });
7
8 it('should remove interval', () => {
9 global.clearInterval.calledOnce.should.be.true;
10 });
11 });
12 ...
And now the server scenarios responding successfully and failed. For this, we will assign our data
from the fixtures folder to variables.
First project: Build Checker 37
1 ...
2 const fs = require('fs');
3 const successResponseCI = fs.readFileSync(__dirname + '/fixtures/success.xml', 'utf8\
4 ');
5 const errorResponseCI = fs.readFileSync(__dirname + '/fixtures/error.xml', 'utf8');
6 ...
Note that in each of the success and failure cases we are using the sinon.useFakeTimers method,
which is a way to simulate events linked to timer objects in Javascript.
When we call the clock.tick the method with the information contained in the configuration file,
we simulate time and time changes at the time of testing, which helps us force the call to the polling
event, which uses setInterval.
1 ...
2 clock = sinon.useFakeTimers();
3 clock.tick(CONFIG.INTERVAL);
4 ...
We now use the syntax stub method for the node-request package. With this, we can change the
return when the request.get method is called. In this case, we can simulate the response of each
request, based on the information of our fixtures.
1 ...
2 sinon.stub(request, 'get').yields(null, null, successResponseCI);
3 ...
An important point in our tests is to remember to restore all the stub and fakeTimers information.
We will use the afterEach method that is always called after each it method, and we will add our
objects by calling the restore method added by sinon.
1 ...
2 afterEach(() => {
3 request.get.restore();
4 clock.restore();
5 });
6 ...
Based on our test scenarios, this is the test content of our build checker file:
First project: Build Checker 38
1 // test/build-checker.js
2
3 const BuildChecker = require('../src/build-checker');
4 const CONFIG = require('../src/configuration');
5 const five = require('johnny-five');
6 const request = require('request');
7 const sinon = require('sinon');
8 const fs = require('fs');
9 const successResponseCI = fs.readFileSync(__dirname + '/fixtures/success.xml', 'utf8\
10 ');
11 const errorResponseCI = fs.readFileSync(__dirname + '/fixtures/error.xml', 'utf8');
12 let clock = null;
13
14 describe('BuildChecker', () => {
15
16 beforeEach(() => {
17 buildChecker = new BuildChecker();
18 });
19
20 it('should have the led success port configured', () => {
21 (buildChecker.ledSuccess instanceof five.Led).should.be.equal(true);
22 });
23
24 it('should have the led error port configured', () => {
25 (buildChecker.ledError instanceof five.Led).should.be.equal(true);
26 });
27
28 describe('#stopPolling', () => {
29 beforeEach(() => {
30 sinon.spy(global, 'clearInterval');
31 buildChecker.stopPolling();
32 });
33
34 it('should remove interval', () => {
35 global.clearInterval.calledOnce.should.be.true;
36 });
37 });
38
39 describe('#startPolling', () => {
40 beforeEach(() => {
41 sinon.spy(global, 'setInterval');
42 buildChecker.startPolling();
43 });
First project: Build Checker 39
44
45 afterEach(() => {
46 global.setInterval.restore();
47 });
48
49 it('should creates polling', () => {
50 global.setInterval.calledOnce.should.be.true;
51 });
52
53 describe('When the CI server send success response', () => {
54 beforeEach(() => {
55 clock = sinon.useFakeTimers();
56 sinon.stub(request, 'get').yields(null, null, successResponseCI);
57 sinon.spy(buildChecker.ledSuccess, 'on');
58 sinon.spy(buildChecker.ledError, 'off');
59 buildChecker.startPolling();
60 clock.tick(CONFIG.INTERVAL);
61 });
62
63 afterEach(() => {
64 request.get.restore();
65 clock.restore();
66 });
67
68 it('should turn on the success led', () => {
69 buildChecker.ledSuccess.on.calledOnce.should.be.true;
70 });
71
72 it('should turn off the error led', () => {
73 buildChecker.ledError.off.calledOnce.should.be.true;
74 });
75
76 });
77
78 describe('When the CI server send error response', () => {
79 beforeEach(() => {
80 clock = sinon.useFakeTimers();
81 sinon.stub(request, 'get').yields(null, null, errorResponseCI);
82 sinon.spy(buildChecker.ledError, 'on');
83 sinon.spy(buildChecker.ledSuccess, 'off');
84 buildChecker.startPolling();
85 clock.tick(CONFIG.INTERVAL);
86 });
First project: Build Checker 40
87
88 afterEach(() => {
89 request.get.restore();
90 clock.restore();
91 });
92
93 it('should turn off the success led', () => {
94 buildChecker.ledSuccess.off.calledOnce.should.be.true;
95 });
96
97 it('should turn on the error led', () => {
98 buildChecker.ledError.on.calledOnce.should.be.true;
99 });
100
101 });
102
103 });
104
105 });
This is just one of several unit testing formats for your application. With this, we finish our first
project with unit tests based on our possible scenarios, but if you want to download or fork the
final code, access the build checker project repository in Github³⁹. Let’s go to our next project with
Nodebot and Johnny-five?
³⁹https://github.com/willmendesneto/build-checker
Second project: Fire alarm
Our second example project will be that of an intelligent fire alarm. Our fire alarm will check for
the temperature and, in the case of a fire, it will activate the audible alarm and send an SMS to the
registered cell phone.
A simple example, but that shows some interesting integration points, such as integration with API’s,
data reading of a temperature sensor and integration with the Piezo sound sensor.
Materials needed
For this project we will use:
• 1 Protoboard: A protoboard is nothing more than a plate with holes and conductive connections
for mounting of experimental electrical circuits, without the need of welding;
• 1 Piezo alarm sensor: It will be used for sound feedback to the end user, in our case to the tenant
(s) of the property. This sensor is very simple and costs less than $ 2.00 can be found in any
electric store;
• 1 temperature sensor: Johnny-Five works with a wide range of temperature sensors. In this
case, we will use the LM35 temperature sensor. This sensor is very simple and costs less than
$ 1.50 can be found in any electric store;
Second project: Fire alarm 42
• For a complete list of supported sensors, go to Johnny’s Temperature Sensor Wiki⁴⁰ page and
check the list of sensor reference codes.
Some sensors require a specific voltage port for their correct operation (some sensors call VND). In
our case we will use a 5 volt port for the temperature sensor.
Other sensors may need an analog port. Analog ports are used for the sensor to send voltage data
to the Arduino so we can read and interpret your information.
For this project we will mount the sensors in the Arduino as follows:
• Piezo alarm sensor: Attach the black piezo wire to the GND port and the red wire to our
arduino’s number 3 port;
• LM35 temperature sensor: attach the grounding pin to the GND port, the voltage pin on the
5-volt port and the data output pin on the analog port “A0”;
The following image illustrates the assembly of the integrated components with the Arduino.
⁴⁰https://github.com/rwaldron/johnny-five/wiki/thermometer
Second project: Fire alarm 43
• controller: Name of the sensor used. You can refer to the complete list of sensors supported
by Johnny Five in the Thermometer class documentation⁴¹;
• pin: The pin information used for the analog connection on the Arduino. It is used in analog
sensors for reading the temperature information;
• toCelsius: An optional method that we can rewrite to handle the analog data and transform
it into the temperature format of your preference. In our case, we will use the Celsius format;
Based on this information, the main file of our fire alarm will be:
Let’s then validate the functionality of our code with the Arduino platform. Inside the folder of our
project, we will enter in our command line/prompt/terminal the following command:
1 $ node src/index.js
The code is quite simple, as you can see. In the next topic, we will think a little more about our
architecture and how to evolve this code for something easier to maintain.
1 // src/configuration.js
2 module.exports = {
3 FIRE_ALARM: {
4 // https://github.com/rwaldron/johnny-five/wiki/thermometer
5 CONTROLLER: 'LM35',
6 PIN: 'A0'
7 },
8 INTERVAL: 1000
9 };
This is the contents of our src/fire-alarm.js file. Notice that we are now invoking the external
configuration code and adding the values in the CONFIG variable. This step is interesting because we
unlink the basic settings of our class project, which now has the responsibility of dealing with the
sensors of the fire alarm project.
Second project: Fire alarm 46
1 // src/fire-alarm.js
2 const CONFIG = require('./configuration');
3 const five = require('johnny-five');
4 let intervalId = null;
5
6 function FireAlarm() {
7 this.temperatureSensor = new five.Thermometer({
8 controller: CONFIG.FIRE_ALARM.CONTROLLER,
9 pin: CONFIG.FIRE_ALARM.PIN
10 });
11 };
12
13 FireAlarm.prototype.stopPolling = () => {
14 clearInterval(intervalId);
15 };
16
17 FireAlarm.prototype.startPolling = () => {
18 const self = this;
19 intervalId = setInterval(function() {
20 console.log('celsius: %d', self.temperatureSensor.celsius);
21 }, CONFIG.INTERVAL);
22 };
23
24 module.exports = FireAlarm;
And our main src/index.js file will have a simpler content, having the responsibility of initiating
the project and polling the FireAlarm class instance.
1 // src/index.js
2 const FireAlarm = require('./fire-alarm');
3 const five = require('johnny-five');
4 const board = new five.Board();
5
6 board.on('ready', () => {
7 fireAlarm = new FireAlarm();
8 fireAlarm.startPolling();
9 });
When we run our code from the command, we will see the same result at our command line/prompt.
1 $ npm start
Second project: Fire alarm 47
With these changes, we have a code of easy maintenance and much more readability to be used in
our application. Of course, this is one of the several approaches that can be used in your project, but
the focus of this topic is to pass on the idea of always thinking about how to improve our project.
1 ...
2 this.piezo = new five.Piezo(3);
3 ...
This class accepts the pin number that is bound to the sensor and when activating we have the play
method, which accepts an object with the information:
You can see an example of the play method being used in the code below.
1 self.piezo.play({
2 song: [
3 ['G5', 1/4]
4 ],
5 tempo: 200
6 });
In our project, we are going to make the integration divided into 2 parts. First, we will add the piezo
instance in the constructor of our class so that the piezo is accessible in the other methods.
Second project: Fire alarm 48
1 ...
2 function FireAlarm() {
3 this.piezo = new five.Piezo(3);
4 ...
5 }
6 ...
With our instance added and accessible, let’s use it in our startPolling method. Let’s add
another validation by accessing the piezo.isPlaying boolean that contains the piezo initialization
information in our project and, if the temperature is over the threshold and the piezo is accessible,
we will trigger our audible alarm. With this, the method will remain as the following code.
1 ...
2 FireAlarm.prototype.startPolling = () => {
3 self = this;
4 intervalId = setInterval(function() {
5 if (self.temperatureSensor.celsius >= CONFIG.FIRE_ALARM.LIMIT && !self.piezo.isP\
6 laying) {
7 self.piezo.play({
8 song: [
9 ['G5', 1/4],
10 [null, 7/4]
11 ],
12 tempo: 200
13 });
14 console.log(`Up to the limit: ${self.temperatureSensor.celsius}`);
15 } else {
16 console.log(`That's ok: ${self.temperatureSensor.celsius}`);
17 }
18 }, CONFIG.INTERVAL);
19 };
20 ...
With this, we have the first feedback for the users of our application. The next step will be to add
the SMS sending functionality using the Twilio API.
Second project: Fire alarm 50
It has some paid numbers if you really want to use some product. In our case, we will use the number
generated by the service itself and create a trial account on the platform.
⁴²https://twilio.github.io/twilio-node
Second project: Fire alarm 51
Now, with Twilio set up, let’s start integrating with our fire alarm. First let’s add the phone
information, SSID of your account, and Twilio’s authentication token. Let’s add this information
in our src/configuration.js.
1 module.exports = {
2 FIRE_ALARM: {
3 LIMIT: 30,
4 PIN: 'A0',
5 PHONE_NUMBER: ''
6 },
7 TWILIO: {
8 PHONE_NUMBER: '',
9 ACCOUNT_SSID: '',
10 AUTH_TOKEN: ''
11 },
12 INTERVAL: 1000
13 };
And let’s continue with the integration of Twilio into our code by accessing and reading the
information added in the src/configuration.js file with the Twilio node package.
1 // fire-alarm.js
2
3 const CONFIG = require('./configuration');
4 const request = require('request');
5 const five = require('johnny-five');
6 const twilio = require('twilio');
7 let intervalId = null;
8
9 const client = new twilio.RestClient(CONFIG.TWILIO.ACCOUNT_SSID, CONFIG.TWILIO.AUTH_\
10 TOKEN);
11
Second project: Fire alarm 52
12 function FireAlarm() {
13 this.piezo = new five.Piezo(3);
14 this.temperatureSensor = new five.Thermometer({
15 pin: CONFIG.FIRE_ALARM.PIN
16 });
17 };
18
19 FireAlarm.prototype.stopPolling = () => {
20 clearInterval(intervalId);
21 };
22
23 FireAlarm.prototype.startPolling = () => {
24 self = this;
25 intervalId = setInterval(function() {
26 if (self.temperatureSensor.celsius >= CONFIG.FIRE_ALARM.LIMIT && !self.piezo.isP\
27 laying) {
28
29 self.piezo.play({
30 song: [
31 ['G5', 1/4],
32 [null, 7/4]
33 ],
34 tempo: 200
35 });
36
37 client.messages.create({
38 body: 'Something is wrong with your fire alarm. Please, call the local fire \
39 brigade.',
40 to: CONFIG.FIRE_ALARM.PHONE_NUMBER,
41 from: CONFIG.TWILIO.PHONE_NUMBER
42 });
43
44 console.log(`Up to the limit: ${self.temperatureSensor.celsius}`);
45 } else {
46 console.log(`That's ok: ${self.temperatureSensor.celsius}`);
47 }
48 }, CONFIG.INTERVAL);
49 };
50
51 module.exports = FireAlarm;
Let’s now run our code by typing the command npm start and this will be the information printed
at our prompt/command line.
Second project: Fire alarm 53
When we execute our code and the temperature exceeds the configured limit, in addition to the
Piezo sensor that will trigger the alarm our customer of Twilio will be activated and will send us
an SMS using the previously added configurations. Then you will receive the following message on
your cell phone.
With this we have seen the complete integration of our Fire Alarm. Of course, this is the first step,
you can evolve the code and add new features. As we already know: the sky is the limit!
But what about the unit tests? We know it’s working, but we have to make sure the code has an
acceptable level of quality even to evolve our project and add new features. Let’s now create our
unit tests.
1 $ mkdir test
2 $ npm install --save-dev mocha sinon should
For our tests, we will reuse the contents of test/spec-helper.js and test/mocha.opts that we
created for our build checker project. It uses the mock-Firmata package⁴⁵ to setup the tests in our
Johnny-Five application.
⁴³http://sinonjs.org
⁴⁴https://shouldjs.github.io
⁴⁵https://github.com/rwaldron/mock-firmata
Second project: Fire alarm 54
1 //test/spec-helper.js
2 require('should');
3 const mockFirmata = require('mock-firmata');
4 const five = require('johnny-five');
5
6 const board = new five.Board({
7 io: new mockFirmata.Firmata(),
8 debug: false,
9 repl: false
10 });
1 --reporter spec
2 --recursive
3 --require test/spec-helper.js
4 --slow 1000
5 --timeout 5000
One way to validate when the fire alarm should trigger the audible alarm is to create a stub for
trigger and use some spies for the Twilio API.
Second project: Fire alarm 55
As a first step, we will create the tests of our configuration file, which we will divide between
the temperature sensor information and the Twilio API. We will make the request for our
src/configuration.js and we will check the information of the pin to be connected to the sensor,
the value of the interval to be used to check the sensor information and the acceptable temperature
limit of the environment in which FireAlarm will be working and your phone will be set to receive
SMS.
Now we evaluate the information that will be passed to Twilio, which in our case will be the SSID
key, authentication token and the telephone number provided by Twilio.
1 ...
2 describe('SMS information', () => {
3
4 it('should have account ssid configured', () => {
5 CONFIG.TWILIO.should.have.property('ACCOUNT_SSID').which.is.a.String()
6 });
7
8 it('should have auth token configured', () => {
9 CONFIG.TWILIO.should.have.property('AUTH_TOKEN').which.is.a.String()
10 });
11
12 it('should have the user phone configured', () => {
13 CONFIG.TWILIO.should.have.property('PHONE_NUMBER').which.is.a.String()
Second project: Fire alarm 56
14 });
15 });
16 ...
38
39 });
Let’s then create the scenario to validate the code of our FireAlarm. One of the things we should
keep in mind about our unit testing framework is that its beforeEach method, which happens before
each it method, is going to be used as documentation for playback of each specific scenario.
Let’s then explain more about the contents of this file and why of each test. We create the tests of
the instance of our FireAlarm and its initial attributes.
Let’s validate when we stop our polling. Let’s now use the spy method of the sinon to check if
the code used the clearInterval method to end with the requests. For this, we will check if the
global.clearInterval was used once, by accessing the boolean calledOnce, which is an internal
counter added by the sinon.spy method for the tests.
1 ...
2 describe('#stopPolling', () => {
3 beforeEach(() => {
4 this.sandbox.spy(global, 'clearInterval');
5 fireAlarm.stopPolling();
6 });
7
8 it('should remove interval', () => {
9 global.clearInterval.calledOnce.should.be.true;
10 });
11 });
12 ...
And now the scenarios that happen when the sensor is started. For this, we will assign some spies
for the setInterval functions and for the piezo sensor. Our first validation will be to certify that the
range is being started when we call the fireAlarm.startPolling() method.
1 ...
2 describe('#startPolling', () => {
3 beforeEach(() => {
4 this.piezoPlaySpy = this.sandbox.spy();
5
6 const piezoStub = {
7 isPlaying: false,
8 play: this.piezoPlaySpy
9 };
10 this.sandbox.stub(fireAlarm, 'piezo', piezoStub);
11
12 this.sandbox.spy(global, 'setInterval');
13 fireAlarm.startPolling();
14 });
Second project: Fire alarm 59
15
16 afterEach(() => {
17 global.setInterval.restore();
18 });
19
20 it('should creates polling', () => {
21 global.setInterval.calledOnce.should.be.true;
22 });
23 });
24 ...
To validate the temperature case above the limit we will use the clock.tick method with the
information contained in the configuration file, simulating the time and time changes at the time of
the test, which helps us to force the event Polling, which uses setInterval.
In this scenario we use the stub to simulate the return of the sensor with a value above the acceptable
and we make sure that the Piezo sensor and the Apollo of Twilio were triggered.
1 ...
2 describe('When the temperature is up to the limit', () => {
3
4 beforeEach(() => {
5 clock = this.sandbox.useFakeTimers();
6
7 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
8 celsius: CONFIG.FIRE_ALARM.LIMIT + 1
9 });
10 fireAlarm.startPolling();
11 clock.tick(CONFIG.INTERVAL);
12 });
13
14 afterEach(() => {
15 clock.restore();
16 });
17
18 it('should trigger piezo sensor alarm', () => {
19 this.piezoPlaySpy.calledOnce.should.be.true;
20 });
21
22 it('should send the SMS to user', () => {
23 this.createMessagesSpy.calledOnce.should.be.true;
24 });
25
Second project: Fire alarm 60
26 });
27 ...
Now we will validate the ambient temperature scenario in acceptable levels. The checks use the
same approaches as the previous one, but in this case, we make sure that the piezo sensor and the
Twilio API were not triggered.
1 ...
2 describe('When the temperature is NOT up to the limit', () => {
3
4 beforeEach(() => {
5 clock = this.sandbox.useFakeTimers();
6
7 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
8 celsius: CONFIG.FIRE_ALARM.LIMIT - 1
9 });
10
11 fireAlarm.startPolling();
12 clock.tick(CONFIG.INTERVAL);
13 });
14
15 afterEach(() => {
16 clock.restore();
17 });
18
19 it('should NOT trigger piezo sensor alarm', () => {
20 this.piezoPlaySpy.calledOnce.should.be.false;
21 });
22
23 it('should NOT send the SMS to user', () => {
24 this.createMessagesSpy.calledOnce.should.be.false;
25 });
26
27 });
28 ...
Based on our test scenarios, this is the test content of our fire alarm file:
Second project: Fire alarm 61
With that we finished our first project with unit tests, covering all our possible scenarios. In the next
chapter, we’ll look at other services that make life easier for us by sending the code to production,
such as seamless integration servers, code coverage, and complexity in an automated way.
Supporting Your Code on Multiple
Operating Systems
In this stage of the book, we will then validate and verify the coverage of tests in our project on
different operating systems, as well as enable different web services for workflow improvements,
such as continuous integration tools and code coverage.
This step is very important because these tools help us in the security process of our code, checking
different criteria of acceptance of our application in an automated way.
After this step, you will be redirected to a new page with all your repositories. To add a new one
just click on the “+” icon next to the text “My Repositories”.
Now, you will be redirected to a new page with all your repositories. To add a new one just click on
the “+” icon next to the text “My Repositories”.
This next step is very simple since the page has a tutorial showing each of the steps to enable the
integration of Travis-CI with its repository in Github, as we can see in the image below.
Supporting Your Code on Multiple Operating Systems 66
On the same page, all your repositories will be listed so you can choose and enable Travis-CI
integration with your project. To enable it, just click the grey button with an “X” and when it changes
colour to green it means that everything went as expected and its repository is synchronised with
Travis-CI.
Travis-CI is fully configurable and you can add information from a wide range of commands, from
commands to be invoked before, during or after the build, and even configure the types of operating
systems that the tasks should take place.
These settings will be in the .travis.yml file that will be in the root folder of our project. Let’s
explain a bit more about configuring these tasks in Travis-CI.
First, in the .travis.yml file, we will add the os field, with the appropriate information of the
operating systems used for our tests.
1 ...
2 os:
3 - linux
4 - osx
5 ...
We will also add the "node_js" field, which will be our information about the NodeJS versions that
Supporting Your Code on Multiple Operating Systems 67
the tasks should be used in our tasks. In our case, we will only add one version, but we could add
several others based on our support needs, for example.
1 ...
2 node_js:
3 - '12.16.2'
4 ...
Our continuous integration server is nothing more than a container with a complete operating
system. So we can also configure environment variables in it. In this case, we will add the variable
NO_SERIALPORT_INSTALL, specifying that we should not install the ‘serialport’ package in this case
because it is a test that uses a mock of a physical board.
NOTE: The idea of this book is to focus on the concepts directly related to Nodebots and integrations
with the javascript repository created, so I will not explain the concept of containers. If you want
to know more about this concept used by Travis-CI, visit the official Docker project website⁴⁸.
1 ...
2 env:
3 - NO_SERIALPORT_INSTALL=1
4 ...
We can also define the set of tasks that will be used before and after our Travis script. In this case,
we will use before for the commands that must occur before our main script and after for the
commands that must occur after the Travis commands, as you can see in the following code snippet:
1 ...
2 before_script:
3 - 'npm install'
4
5
6 after_script:
7 - 'make test'
8 ...
In this case, we are installing our dependencies and running our tests. All this in a very simple and
well-defined way. The contents of our .travis.yml file with all the changes will be as follows:
⁴⁸https://www.docker.com
Supporting Your Code on Multiple Operating Systems 68
1 language: node_js
2 os:
3 - linux
4 - osx
5 node_js:
6 - '12.16.2'
7 before_script:
8 - 'npm install'
9
10
11 after_script:
12 - 'make test'
13 env:
14 - NO_SERIALPORT_INSTALL=1
We can see that the Travis-CI build is a bit different now since we are running the same setup on
Linux and OSX operating systems, identified by the icons of each operating system.
With the integration tested, let’s then put the Travis-ci badge in our README.md file in the repository.
With this, you will see an image with the status of the build.
1 [](https://travis-ci.org/willmendesneto/build-checker)
With this, we have finished our integration with Travis-CI continuous integration server and we
have our entire suite of tests running on Linux and OSX systems. In this next step we will configure
the same tasks, but to be verified by the Windows operating system, using another continuous
integration server called Appveyor.
The continuous integration service Appveyor⁴⁹ is one of the solutions used for testing projects hosted
on GitHub in Windows environments, facilitating this process and ensuring that our code is cross-
Platform⁵⁰, running on major operating systems.
Adding Appveyor support to our project is a fairly simple task. We will then visit the official website
of the project and create a login with our information.
Appveyor Site
On the login page, we have some options listed with support for some of the major code repositories
on the internet. In this option we will use Github, to facilitate the next steps, but it is worth
remembering that you can use any of the options supported for login.
⁴⁹https://www.appveyor.com/
⁵⁰https://en.wikipedia.org/wiki/Cross-platform
Supporting Your Code on Multiple Operating Systems 70
Logging in to Appveyor
With your user created and your access working, the main page after login is a page listing all of
your repositories based on a category located on the left side of the site. In our case, we will choose
the build-checker project and click on the ‘Add’ button to add the support to our project.
We will then see the page of our project with the information specific to it, currently.
Now that we have our project configured we will create our appveyor.yml file, where our test settings
will be. This file is very similar to Travis-CI in some ways.
The contents of our appveyor.yml file with all the changes will be as follows:
We will add the version of NodeJS used in the environment field of our configuration file.
Supporting Your Code on Multiple Operating Systems 71
1 ...
2 environment:
3 matrix:
4 - nodejs_version: "12"
5 ...
The platform field will be used to describe the platforms used. Note that in this case instead of having
the differentiation between operating systems, we have between operating system platforms (x86
and 64x).
This is also interesting if there is a need to have a notion of the difference in performance,
performance and other aspects of the same application on different platforms.
1 ...
2 platform:
3 - x86
4 - x64
5 ...
The install field will list our initial commands. Note that ps is a command to install NodeJS with the
one specified in the file.
After this step we cleared the cache using the npm cache clean command for security measure in
order to avoid possible false positives in our tests and, after completing this command, we will then
install our dependencies using the npm install command.
1 ...
2 install:
3 - ps: Install-Product node $env:nodejs_version
4 - npm cache clean --force
5 - npm install
6 ...
The test_script field will have the list of our commands to execute at the time of running our tests.
We are directly accessing the node_modules folder and invoking the tests from them with the node_-
modules/.bin/Istanbul cover node_modules/mocha/bin/_mocha -- -R dot command, because we
use the make test command in our npm test.
1 ...
2 test_script:
3 Run the test
4 - cmd: node_modules/.bin/istanbul cover node_modules/mocha/bin/_mocha -- -R dot
As our case does not require the creation of a build, we will add the information in our file with
the off value and we will configure our build to be finalised as soon as possible by adding the
fast_finish field with the value true.
Supporting Your Code on Multiple Operating Systems 72
1 build: off
2 matrix:
3 fast_finish: true
4 ...
The final content of our appveyor.yml file with all changes will be as follows:
Notice that in this case, we have the list of our differentiated build by platforms on the listing page.
With the new integration tested, we will then update the README.md file from the repository with
the Appveyor badge. With this, you will see an image with the status of the build, as well as what
we have inserted previously.
Notice that we have two tags in this code snippet. Replace this information as follows:
For example, based on the example repository, our badge will have the following content.
As you might realise adding support for multiple operating systems and platforms is quite a simple
task with Appveyor. The next steps in the book will be more focused on improving the automation
of checking our code coverage.
⁵¹http://gotwarlost.github.io/istanbul
⁵²Https://en.wikipedia.org/wiki/Reverseengineering
Supporting Your Code on Multiple Operating Systems 74
To verify that it is integrated into our repository, simply type in our prompt/command line.
1 $ ./node_modules/.bin/istanbul help
Supporting Your Code on Multiple Operating Systems 75
As we previously integrated MochaJS⁵³ into our repository when we created the project tests, we
can simply type the following command at our command prompt.
⁵³https://mochajs.org/
Supporting Your Code on Multiple Operating Systems 76
The return will be the same as the image below. You may notice that we now have some new
information in the footer of test messages, such as percentages of rows, functions, branches, and
declarations of methods, classes, or objects.
Supporting Your Code on Multiple Operating Systems 77
Notice that we now have a new folder called coverage with some files and all this information listed
in our command line. We will use them in the next steps for integration with the Coveralls service.
Supporting Your Code on Multiple Operating Systems 78
The login is very simple and you will have to enable integration with your Github. After this step,
you will see a list with all your repositories registered in Github. Click the button to the left of your
listed repository and wait for the message "Off" to become "On".
⁵⁴https://coveralls.io/
Supporting Your Code on Multiple Operating Systems 79
Adding repositories
Note that with the repository enabled, we now have a link to the details page. By clicking this link
we will be directed to a page with all the initial information for the project setup in coveralls. For
our solution, we will use the option to add coveralls information to the .coveralls.yml file.
We will then copy this content from the file option on the setup page and create the new file in
our project. Within our local repository, we will type the following command via prompt/command
line.
1 $ touch .coveralls.yml
We will open this file in our editor and we will add the content to this file. After this step, we will add
the NodeJS package to our list of development dependencies to integrate the coveralls infrastructure
into our project by typing the following command.
Supporting Your Code on Multiple Operating Systems 80
Once we submit a new code, we can see that we have the percentage of code coverage information
visible on the coveralls website in the area of our repository. With this we can follow all variations
of code coverage, we create validations and more.
After that, we can add a new badge with the code coverage information for our project in the
README.md the file contained in the project repository. The badge pattern is quite simple:
1 [](https://coveralls.io/r/<nome-do-\
3 seu-usuario-ou-organização>/<nome-do-seu-repositório>?branch=master)
Notice that we have two tags in this code snippet. Replace this information as follows:
For example, based on the example repository, our badge will have the following content.
1 [](https://coveralls.io/r/willmendesneto/build-checker?branch=master)
After adding and saving this code, the final result to be rendered will be something similar to the
following image.
Supporting Your Code on Multiple Operating Systems 81
And with this, we conclude our integration with the coveralls service. This is just a simple example
of one of the many features of this service and I strongly recommend that you read the coveralls
documentation⁵⁵ so that you have a greater This service.
If you would like to know more about PlatoJS, please visit the repository in the Github of
the project⁵⁶
And after this step, the plato was installed locally as a development dependency in our node_-
modules folder of our project. Our next step is to add a new NPM command. Now we will have the
code-analysis command that will trigger the plate to our project.
1 {
2 ...
3 "scripts": {
4 "start": "nodemon ./src/index.js -e js,json --watch ./src",
5 "test": "make test",
6 "code-analysis": "plato -r -d report src test"
7 },
8 ...
9 }
And after this command will be created a folder of name report with the information of the analysis
of our repository.
⁵⁵https://coveralls.zendesk.com/hc/en-us
⁵⁶https://github.com/es-analysis/plato
Supporting Your Code on Multiple Operating Systems 82
Within this folder, we will have several files with the information returned from the PlatoJS analysis
that we can see more details by accessing the index.html file in our browser.
This page will have information on each file and graphs showing data such as level of complexity
and lines of code, as we can see in the figure below.
Supporting Your Code on Multiple Operating Systems 83
Breadboard
Piezo
Piezo is a sensor that emits a beep, such as a horn. This signal can be created in an application
Nodebots from a numeric value or an abstraction of sound/music notes, which makes their
Appendix 85
manipulation simpler.
Typical uses of buzzers include alarm devices, timers, and confirmation of user input such as a mouse
click or keystroke.
Piezo
Appendix 86
Resistors
The resistors are widely used in electronics as one of the first electronic components that users have
the first contact and one of the most used. They are small enough in pill form with stripes in most
cases. Because resistance is an essential element of nearly every electronic circuit, you’ll use them
in just about every circuit that you build.
A resistor is an electronic component that limits the flow of electrons dissipating energy in the form
of heat, since the electricity has to struggle to flow through something with a high resistance. It uses
a large amount of energy and converting it into heat.
Resistors
Light-emitting diode
Sensors
A device that converts real-world (analog) data into data that a computer can understand using
ADC and converting data from Analog to Digital format. We will use sensors to detect events or
environment changes and we will send it for you to read in our application.
Jumper Wires
It is a short insulated wire with bare (stripped of insulation) ends. You use them to connect two
points in a breadboard circuit.
Jumpers
Push Button
It is a simple switch mechanism for controlling some aspect of a machine or a process.
Push button
Next steps
Finishing now the contents of this book with some rather didactic examples we already see the power
of Javascript allied to robotics in our applications. These are examples that can be incorporated into
our daily lives and teachings to carry out various other ideas that will arise.
I hope you have enjoyed the contents of this book and the examples as much as I have had the
pleasure of sharing this content. If you have questions, questions or even a conversation, please
contact us at [email protected].
Thank you very much!