Using Gitlab Pages

Working professionally everyday with Gitlab has made me think twice of hosting my blog on an isolated Digital Ocean Droplet. Especially as we are promoting Devops and Continuous Delivery (for which I’ve been attending a training by Jez Humble last week!)

So I’ve converted my blog to use Gitlab Pages. This means my entire blog resides in texfiles in Gitlab(.com) and whenever I push a new version of the code, the Gitlab runners build my blog using a Static Site Generator (Hexo).This sounds confusing I know, but I will explain in the coming week.

Blog Pipeline

Sien is boos

Plaatje Sien is boos

Als je er op gaat letten, is het opvallend dat kinderboeken zo vol staan met stereotiepe rolpatronen. Stichting Zo-ook heeft hier onderzoek naar gedaan en wil kennis en bewustwording vergroten en ook het aanbod niet-stereotiepe kinderboeken vergroten.

Daarom zijn zij pas geleden met een crowdfunding campagne gestart om het uitgeven van een boek met niet-stereotiepe rolpatronen mogelijk te maken. Zij willen hiermee laten zien dat het ook anders kan. Niet met het argument dat het ene rolpatroon goed is en het andere slecht, maar om ervoor zorgen dat kinderen meerdere rolpatronen zien, dat ze andere rolmodellen zien. Dat ze zich kunnen inleven in iedereen, dat ze niet leren dat bepaalde activiteiten (of studies of banen) niet geschikt zijn voor de ene sekse, of dat de ene groep belangrijker is dan de andere.

Het boek Sien is boos, geschreven door schrijfster Liesbeth Mende, is net even anders dan anders, maar het ligt er niet dik bovenop. Het is vooral een aansprekend verhaal voor kinderen die net beginnen met lezen (Avi3 niveau).
Bij voldoende donateurs kan het worden gerealiseerd.

De campagne loopt nog twee weken. Het zou heel mooi zijn als het lukt!

Contactgegevens:
Carolien Jaspers
Stichting Zo-ook (Zo kan het ook!) voor diversiteit in kinderboeken
Website: www.zo-ook.info
Twitter: @zo_ook

Down and out in Katowice

[First code] []
1
2
3
4
var express = require('express', template = require('pug'));
var session = require('express-session')
var util = require('util');
var oauth = require('oauth');

So what is this? It is the first code I used for the backend part of our project at the ING Hackathon 2017 in Katowice, Poland. It was held at Walcownia Cynku,a former Zinc rolling Mill (big scary machines!).

logo
The logo

I was part of a team of 6 people and we had a great time. We arrived thursday afternoon and had a nice evening (I also met our colleagues from the CDaaS support team who are based in Katowice)
teampic

The countdown

Unfortunately our team did not win the competition, but one of us won a prize for best photo’s and it was a great experience above all!

wal1
The venue

wal2
Don’t go near this!

Creating Excel sheets with java

Currently I am working on a project where we need to create Excel reports from a datasource. There are several ways to do this programmatically, but our first thought was to do this in Java. We actually abandoned this path later on and went for something with Nodejs, but I’ll share my thoughts on the Java road nonetheless.

There are several Java libraries available which enable read-write of Excel sheet formats.

But wouldn’t it be great to have an Excel template that can be used by non-programmers to create a report and that your program can fill this with data?

Enter Jxls, you can use this library to achieve this.

The default example shows how to create an Excel report with a collection of Java objects.

I specifically wanted to know if it would be possible to iterate through child objects and create rows with those as well. Earlier versions of Jxls could not handle this the way I wanted.

So I altered the demo a bit to answer my question. Maybe someone will benefit from this as well.

The Excel template:
Excel template

The result:
Excel report

Here is a link to the altered demo on github.

Teach 'em

I truly believe coding software should be part of the curriculum of young people. Kids can learn to express themselves digitally.

Currently the primary school that my kids attend does not have resources for this kind of tuition.

So I decided to offer my assistance in organising two courses (2x 2 hours) in Scratch programming. Scratch is a visual programming language designed by MIT media labs in 2003.

The kids were able to create some nice games in that timeframe!

Really proud!

studio

Part 3: Running CI on Sauce Labs

I have started a small github project for hosting a demo test.

In this project there is an Appium test for the TodoMVC app we built in earlier posts and this test can be run on Sauce Labs with accompanying scripts. First thing is to get a trial account at Sauce Labs.

BTW, Sauce Labs, Inc. is at Microsoft’s Build conference right now and they have announced better support for Visual Studio (with a plugin) and the fact that Microsoft itself is going to support the same automation API (the JSON Wire Protocol).

Anyway, back to the example. Let’s start by cloning the example test. Let’s pretend you build the app from the earlier posts.

Clone the TodoMVC example test

1
2
3
4
5
6
7
8
9
10
cd $HOME/tmp/blogpost
git clone https://github.com/Joustie/appium-tests.git
cd appium-tests
#The tests use mocha and WD such, so they need to be installed
npm install

Upload the app package to Sauce Labs

So now we are ready to upload the earlier created iOS build to Sauce Labs, in what they call ‘sauce-storage’. It’s storage associated with your Sauce Labs account, nobody but you can reference and use it and it’s temporary.

Open the file upload_todo.sh to configure the script with your settings.

1
2
3
4
5
6
#change these variables
SAUCE_USERNAME=set this to yours
SAUCE_ACCESS_KEY=set this to yours
#make sure the files are zipped (ususally just zip the .app dir on the mac)
REMOTE_FILE=the referenced name in sauce storage
LOCAL_FILE=the local filename you wish to upload (I would use the absolute filename, but hey, that's just me)

The remote file in my case would be called ‘todo.app.zip’ and the local file $HOME/tmp/blogpost/todomvcios/platforms/ios/build/emulator/TodoMVCiOS.zip
This zip needs to be created by:

1
2
cd $HOME/tmp/blogpost/todomvcios/platforms/ios/build/emulator
zip -r TodoMVCiOS.zip TodoMVCiOS.app

To be clear , TodoMVCiOS.app is the app file created by the build in our earlier post.

Create the Appium test (with Mocha)

Just like the nodejs examples for appium there is a bit of boilerplate code involved to make the test work. You will find a ‘helpers’ directory with functions providing support.

1
2
3
4
5
6
7
8
9
10
11
12
├── LICENSE
├── README.md
├── helpers
│   ├── appium-servers.js --> credentials and such for appium
│   ├── apps.js --> the apps to test are defined in here
│   ├── caps.js --> defines the capabilities for a testrun e.g. the ios version, which device
│   ├── logging.js --> some logging functionality
│   └── setup.js --> some basic testing setup
├── ios-todo-on-sauce-9.2.sh -->shell script to run the test on Sauce Labs
├── ios-todo.js --> the main test file
├── package.json --> the package specification with the basic requirements and config info
└── upload_todo.sh --> this shell scripts uses curl to upload an app package to Sauce Labs storage

An important aspect of the test is finding UI-elements in the app. There are several strategies for that in Appium. In this case I used the Xpath-locater strategy. You can find elements by searching for them in the Appium inspector after starting Appium.
Appium start

After starting Appium you can start the inspector. You can find the xpath of elements in the middle column.

Appium inspector

You can export the list of elements using the ‘copy xml’ button for later use.

When you have identified all elements for your test you can write some javascript.

So the main test file is ios-todo.js. You will find some setup code in there, but the main tests begin halfway and are written in Mocha style.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
it("click to make it editable and fill in value", function () {
return driver
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]").click()
.elementByXPath("//UIAApplication[1]/UIAWindow[4]/UIAKeyboard[1]")
.should.eventually.exist
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]").sendKeys("Test 1 \n")
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[3]")
.should.eventually.exist
});
it("enter another value", function () {
return driver
.elementByXPath("//UIAApplication[1]/UIAWindow[4]/UIAKeyboard[1]")
.should.eventually.exist
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]").sendKeys("Test 2 \n")
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[4]")
.should.eventually.exist
});
it("select first value, mark complete", function () {
return driver
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIASwitch[3]")
.should.eventually.exist
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIASwitch[3]").click()
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]")
.should.eventually.exist
});
it("delete first value", function () {
return driver
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]").click()
});
it("delete second value", function () {
return driver
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]").click()
.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]")
.then(function() {
console.log("found the button");
throw Error('Delete button is still there, where it should not be!');
},function rejectedPromise(){
//throw Error('Delete button for entry is not there!');
})
});
```
### Run the test on Sauce Labs
The main script can be run as:
````language-bash
./ios-todo-on-sauce-9.2.sh
ios simple
> CALL init({"browserName":"","appium-version":"1.5","platformName":"iOS","platformVersion":"9.2","deviceName":"iPad Air","app":"sauce-storage:todo.app.zip","name":"ios - todo","tags":["appium,test"]})
> POST /session {"desiredCapabilities":{"browserName":"","appium-version":"1.5","platformName":"iOS","platformVersion":"9.2","deviceName":"iPad Air","app":"sauce-storage:todo.app.zip","name":"ios - todo","tags":["appium,test"]}}
Driving the web on session: 915c4300e9bf4f9b9f30c0b201c84711
> RESPONSE init({"browserName":"","appium-version":"1.5","platformName":"iOS","platformVersion":"9.2","deviceName":"iPad Air","app":"sauce-storage:todo.app.zip","name":"ios - todo","tags":["appium,test"]}) "915c4300e9bf4f9b9f30c0b201c84711",null
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]") {"ELEMENT":"0"}
> CALL element.click()
> POST /session/:sessionID/element/0/click
> RESPONSE element.click()
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[4]/UIAKeyboard[1]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[4]/UIAKeyboard[1]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[4]/UIAKeyboard[1]") {"ELEMENT":"1"}
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]") {"ELEMENT":"2"}
> CALL element.sendKeys("Test 1 \n")
> POST /session/:sessionID/element/2/value {"value":["Test 1 \n"]}
> RESPONSE element.sendKeys("Test 1 \n")
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[3]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[3]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[3]") {"ELEMENT":"3"}
✓ click to make it editable and fill in value (20955ms)
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[4]/UIAKeyboard[1]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[4]/UIAKeyboard[1]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[4]/UIAKeyboard[1]") {"ELEMENT":"4"}
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIATextField[1]") {"ELEMENT":"5"}
> CALL element.sendKeys("Test 2 \n")
> POST /session/:sessionID/element/5/value {"value":["Test 2 \n"]}
> RESPONSE element.sendKeys("Test 2 \n")
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[4]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[4]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[4]") {"ELEMENT":"6"}
✓ enter another value (20192ms)
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIASwitch[3]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIASwitch[3]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIASwitch[3]") {"ELEMENT":"7"}
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIASwitch[3]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIASwitch[3]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIASwitch[3]") {"ELEMENT":"8"}
> CALL element.click()
> POST /session/:sessionID/element/8/click
> RESPONSE element.click()
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]") {"ELEMENT":"9"}
✓ select first value, mark complete (5070ms)
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]"}
> RESPONSE elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]") {"ELEMENT":"10"}
> CALL element.click()
> POST /session/:sessionID/element/10/click
> RESPONSE element.click()
✓ delete first value (2545ms)
> CALL elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]")
> POST /session/:sessionID/element {"using":"xpath","value":"//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAButton[1]"}
✓ delete second value (1157ms)
> CALL quit()
> DELETE /session/:sessionID
Ending your web drivage..
> RESPONSE quit()
> CALL sauceJobStatus(true)
> POST /rest/v1/:user/jobs/:sessionID {"passed":true}
> RESPONSE sauceJobStatus(true)
5 passing (3m)

The above means all of your tests went ok. If you look closely in the output you’ll see the description of the tests from the mocha test (e.g. ✓ delete second value )

You can view the results in your personal dashboard on Saucelabs.com

Dashboard

There is a video and screenshots of the session if you click on the test.

Todomovie

Part 2: Getting your app in a Cordova container (iOS version)

By choosing for Cordova as an App container, most developers aim to build an app that should run on the iOS and Android platform from one codebase. In the last post I demonstrated how to embed a javascript React App in Cordova on the Android platform, now I will do so for iOS (on a Mac).

A quick recap to make this work (repeating steps from former posts):

$cd $HOME/tmp

$mkdir blogpost

$cd blogpost

$git clone https://github.com/facebook/flux.git

$cd flux/examples/flux-todomvc

$npm install
#lots of installing here

In Part1 and Part2 (Android version) I used the ‘npm start’ command to create the concatenated javascript file bundle.js that is referenced in the index.html. Let’s use the alternative build command to create a minified version of the javascript bundle (more efficient because it deletes newlines, tabs and spaces).

$npm run build

Then for ol’ times sake, let’s use perl to replace the javascript bundle mentioned in index.html, as it defaults to the non-minified version (bundle.js), let’s use the minified version. (Afterwards, you can verify it all works by opening index.html in your browser from the filesystem).

$perl -p -i -e 's/bundle.js/bundle.min.js/g' index.html

Same as before, to embed the React app in a Cordova container, you need Cordova installed, a crossplatform app container.

Install it with:

$npm install -g cordova

Let’s make an empty cordova application

$cd $HOME/tmp/blogpost

$cordova create todomvcios nl.joustie.todomvcios "TodoMVCiOS"

$cp -R flux/examples/flux-todomvc/* todomvcios/www/

$cd todomvcios/www

Now it’s time to add iOS support (this copies the built app and packages it for iOS)

$cordova platform add ios

Then create a build which we can run in the simulator.

$cordova build ios

We now can run the result in the iOS simulator:

$cordova run ios --emulate

iOS simulator

Part 2: Getting your app in a Cordova container (Android version)

When I wrote this part of the series, I started it on a Mac. Along the way I had to write some of it on a different computer, running Linux. So I decided to change the contents a bit of this post. I will still get the App in a container, but I will show it to you for the Android platform as well as iOS. Part 2 will therefore be split in two (an Android part and and iOS part). The reason for this is that it is nearly impossible to run an iOS simulator on Linux, so here you go.

So I will start with Android. You will have to make sure your development setup for compiling Android applications is correct. You can find pointers here

We will start with the React app built in part 1. The result of git clone should be located in $HOME/tmp/blogpost.

Remember when we built this React flux example app, we created a bundled version of all javascript in a file called bundle.js. It’s located in the js/ folder of the project and was created by running the build step ‘npm start’. I am mentioning this, because if you start with a fresh copy of the flux example, this build step needs to be performed for the app to work (the bundle.js is referenced in the index.html file of the project).

Let’s continue:

To embed the React app in a Cordova container, you need Cordova, a crossplatform app container.

Install it with:

npm install -g cordova

Then create the skeleton cordova application and copy the already created React app into it.

cd $HOME/tmp

$cd blogpost

$cordova create todomvc nl.joustie.todomvc "TodoMVC"

$cp -R flux/examples/flux-todomvc/* todomvc/www/

$cd todomvc/www

I don’t use the stock Android emulators that come with the SDK because they are just … too.. slow. Better to grab a copy of Genymotion that uses a Virtualbox virtualization and offers X86 Android (I have heard that the newest emulator that is included with Android Studio is also significantly faster, but I haven’t managed to test this out)

But.. this means you will have to compile for X86 as well, and not only for ARM cpu architecture. For this I used Crosswalk plugin to let the build phase also create X86 packages.

$cordova plugin add cordova-plugin-crosswalk-webview@1.2.0 
Fetching plugin "cordova-plugin-crosswalk-webview@1.2.0" via npm
Installing "cordova-plugin-crosswalk-webview" for android
joost@joost-Studio-XPS-1645:~/tmp/todomvc$ cordova build android
Running command: /home/joost/tmp/todomvc/platforms/android/cordova/build 
ANDROID_HOME=/home/joost//android-sdk-linux
JAVA_HOME=/usr/lib/jvm/java-7-oracle
Running: /home/joost/tmp/todomvc/platforms/android/gradlew cdvBuildDebug -b /home/joost/tmp/todomvc/platforms/android/build.gradle -Dorg.gradle.daemon=true
WARNING [Project: :] Current NDK support is deprecated. Alternative will be provided in the future.
WARNING [Project: :] Current NDK support is deprecated. Alternative will be provided in the future.
WARNING [Project: :] Current NDK support is deprecated. Alternative will be provided in the future.
WARNING [Project: :] Current NDK support is deprecated. Alternative will be provided in the future.
Download https://download.01.org/crosswalk/releases/crosswalk/android/maven2/org/xwalk/xwalk_core_library_beta/13.42.319.12/xwalk_core_library_beta-13.42.319.12.pom
Download https://download.01.org/crosswalk/releases/crosswalk/android/maven2/org/xwalk/xwalk_core_library_beta/13.42.319.12/xwalk_core_library_beta-13.42.319.12.aar
:preBuild
:compileArmv7DebugNdk
:preArmv7DebugBuild
:checkArmv7DebugManifest
:preX86DebugBuild
:CordovaLib:compileLint
:CordovaLib:copyDebugLint UP-TO-DATE
:CordovaLib:mergeDebugProguardFiles UP-TO-DATE
:CordovaLib:preBuild
:CordovaLib:preDebugBuild
:CordovaLib:checkDebugManifest
:CordovaLib:prepareDebugDependencies
:CordovaLib:compileDebugAidl UP-TO-DATE
:CordovaLib:compileDebugRenderscript UP-TO-DATE
:CordovaLib:generateDebugBuildConfig UP-TO-DATE
:CordovaLib:generateDebugAssets UP-TO-DATE
:CordovaLib:mergeDebugAssets UP-TO-DATE
:CordovaLib:generateDebugResValues UP-TO-DATE
:CordovaLib:generateDebugResources UP-TO-DATE
:CordovaLib:packageDebugResources UP-TO-DATE
:CordovaLib:processDebugManifest UP-TO-DATE
:CordovaLib:processDebugResources UP-TO-DATE
:CordovaLib:generateDebugSources UP-TO-DATE
:CordovaLib:compileDebugJava UP-TO-DATE
:CordovaLib:processDebugJavaRes UP-TO-DATE
:CordovaLib:packageDebugJar UP-TO-DATE
:CordovaLib:compileDebugNdk UP-TO-DATE
:CordovaLib:packageDebugJniLibs UP-TO-DATE
:CordovaLib:packageDebugLocalJar UP-TO-DATE
:CordovaLib:packageDebugRenderscript UP-TO-DATE
:CordovaLib:bundleDebug UP-TO-DATE
:prepareAndroidCordovaLibUnspecifiedDebugLibrary UP-TO-DATE
:preArmv7ReleaseBuild
:preX86ReleaseBuild
:CordovaLib:copyReleaseLint UP-TO-DATE
:CordovaLib:mergeReleaseProguardFiles
:CordovaLib:preReleaseBuild
:CordovaLib:checkReleaseManifest
:CordovaLib:prepareReleaseDependencies
:CordovaLib:compileReleaseAidl
:CordovaLib:compileReleaseRenderscript
:CordovaLib:generateReleaseBuildConfig
:CordovaLib:generateReleaseAssets UP-TO-DATE
:CordovaLib:mergeReleaseAssets
:CordovaLib:generateReleaseResValues
:CordovaLib:generateReleaseResources
:CordovaLib:packageReleaseResources
:CordovaLib:processReleaseManifest
:CordovaLib:processReleaseResources
:CordovaLib:generateReleaseSources
:CordovaLib:compileReleaseJava
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
:CordovaLib:processReleaseJavaRes UP-TO-DATE
:CordovaLib:packageReleaseJar
:CordovaLib:compileReleaseNdk
:CordovaLib:packageReleaseJniLibs UP-TO-DATE
:CordovaLib:packageReleaseLocalJar UP-TO-DATE
:CordovaLib:packageReleaseRenderscript UP-TO-DATE
:CordovaLib:bundleRelease
:prepareOrgXwalkXwalk_core_library_beta134231912Library
:prepareArmv7DebugDependencies
:compileArmv7DebugAidl
:compileArmv7DebugRenderscript
:generateArmv7DebugBuildConfig
:generateArmv7DebugAssets UP-TO-DATE
:mergeArmv7DebugAssets
:createXwalkCommandLineFileArmv7Debug
:generateArmv7DebugResValues
:generateArmv7DebugResources
:mergeArmv7DebugResources
:processArmv7DebugManifest
:processArmv7DebugResources
:generateArmv7DebugSources
:compileArmv7DebugJava
:preDexArmv7Debug
:dexArmv7Debug
:processArmv7DebugJavaRes UP-TO-DATE
:validateDebugSigning
:packageArmv7Debug
:zipalignArmv7Debug
:assembleArmv7Debug
:compileX86DebugNdk
:checkX86DebugManifest
:prepareX86DebugDependencies
:compileX86DebugAidl
:compileX86DebugRenderscript
:generateX86DebugBuildConfig
:generateX86DebugAssets UP-TO-DATE
:mergeX86DebugAssets
:createXwalkCommandLineFileX86Debug
:generateX86DebugResValues
:generateX86DebugResources
:mergeX86DebugResources
:processX86DebugManifest
:processX86DebugResources
:generateX86DebugSources
:compileX86DebugJava
:preDexX86Debug
:dexX86Debug
:processX86DebugJavaRes UP-TO-DATE
:packageX86Debug
:zipalignX86Debug
:assembleX86Debug
:assembleDebug
:cdvBuildDebug

BUILD SUCCESSFUL

Total time: 2 mins 17.031 secs

Then start Genymotion (I used the Galaxy S5 image).

When it has started, you can install the compiled package with:

$ adb install android-x86-debug.apk
1557 KB/s (38235054 bytes in 23.980s)
pkg: /data/local/tmp/android-x86-debug.apk
Success

If things went correct, you will find the todomvc application somewhere in you Apps.
Android apps menu
When you start it:
Todomvc running on Android

Part 1: Building a React App

Everyone knows Facebook, the biggest social-networking site. It is one of the largest sites of the planet. For years they have run their site on a customized version of PHP, named Hack. Not so long ago they switched to an internal product for parts of their site. This product is called React, it is a JavaScript framework. It’s star rose quickly internally.

The crux of the product is that it is a component-based, decoupled way of developing software, which is based on the best ideas from computer science about good design.

The abstraction of the DOM (Document Object Model) is handled by using a Virtual Dom. This means that you don’t directly touch the DOM of an HTML document, but rather a virtual DOM, wherein React then determines whether it will update the real DOM. There will be a diff of the change and then React will only find an update of the change position and update.

Last year Facebook published React as Open Source Software and an active community appeared around it. Many large companies have embraced technology and an ecosystem of addons came into being.
React is pure Javascript programming but one can also utilize JSX, a form of XML which can be converted by build tools in pure Javascript. The advantage of this is that a component consists of tree structures which have attributes that can be displayed.

Flux is a design pattern that has emerged as the preferred way to handle data in you React apps. It supports the idea of one-way dataflow. Instead of two-databindings in patterns like MVC, Flux only updates the view in one way. Below is a simplification but it shows clearly the unidirectional flow.

Flux diagram

There are several examples online but let’s take the one from Facebook, the TodoMVC application. It is featured in a tutorial found here.

We are only going to build it and use part of it for a mobile app, so get the source using :

$cd $HOME/tmp
$mkdir blogpost
$cd blogpost
$git clone https://github.com/facebook/flux.git
For now that’s ok, well use the example from this source tree.
$cd flux/examples/flux-todomvc

$npm install
#lots of installing here

Let’s start the project as mentioned in the package.json and have watchify ‘watch’ the file for live changes.
$npm start
> todomvc-flux@0.0.3 start ..../www
> watchify -o js/bundle.js -v -d js/app.js

2906584 bytes written to js/bundle.js (3.05 seconds)

Fire up your browser and open the index.html located in www directory of the flux-todomvc example.

todomvc screenshot

Now open the file Header.react.js in www/js/components
render: function() { return ( <header id="header"> <h1>todos</h1> <TodoTextInput id="new-todo" placeholder="What needs to be done?" onSave={this._onSave} /> </header> ); },
and change the line that says:
placeholder="What needs to be done?"
to
placeholder="What needs to be done, Joustie?"

Now refresh the browser and you will get:
todomvcscreenshot edited

To stop watchify ‘watch’ your live changes just go back to terminal/console/xterm shell session and hit control-c to signal quit.

So now we got this basic app running and I can start writing Part 2 of Running CI end-to-end testing on Saucelabs with a React hybrid cordova app.