A bit more personal

So, this is 2019. What is changing for me? Wel.. I am not going to spill al of my life over here, but I am planning to blog more.

I totally agree with Jaron Lanier that the current social media are flawed. They are based on the ‘freemium’ model using adverts. He argues they are not ‘social’ anymore as we tend to see them but they have become ‘behavioral modification empires’. In his behavorial model the negative impulses get more rewarded than the positive ones, and this is bad.
I personally think this also is true for the schoolyard or playground, so maybe this it is a bit idealistic to think we can limit this.

He also thinks it is not too late. We can correct these mistakes from the 90’s.
Here is his TED-talk about this: https://www.ted.com/talks/jaron_lanier_how_we_need_to_remake_the_internet. We can delete our accounts and use personal sites or paid sites or other ways of celebrating culture and creativity (his words). Or remove the advertisement models, let people pay for things. We don’t need a situation where for two people to communicate a third entity is needed to facilitate this and at the same time manipulates this communication.

So… Let’s make this reality, bringback the blog , making it personal. New ways to connect just like the development of language. I am not as eloquent as Jared but I dig this idea.

I do think we should cherry-pick features from the ‘social media’ and try to reinvent them differently. How do we like each other stories? In the blog era there was ping-back and rss. I am not sure if that is the answer. These things should be simplified if we want people to switch.
What about ‘new social’ networks that promise to protect your privacy better? A new peer-to-peer system to connect personal blogs? The v3 web?

A technique that could aid in this is being developed by Tim Berners Lee new initiative. It aims to safeguard your privacy. The product they are building is called Solid:
‘Solid changes the current model where users have to hand over personal data to digital giants in exchange for perceived value.’

If social media or blogs use this model to protect our sensitive data the world would become a better place!

New social media

New techniques to create media

Just some links about blogs

Together in one world

Last 2 weeks it was ‘ateliermiddag’ again at the Prins Mauritschool in Rijswijk. These events are organised multiple times per year and the pupils of the school can do various small workshops.

Together with other dads I’ve lready given a Scratch ‘programming’ course a few times in recent years. With an emphasis on the creative side to ensure that the younger pupils could join as well. It’s always been a great success.

This year we have come up with a new idea. Working together with a group of pupils in a central Minecraft world to build a model of the school!

school
One of the models

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