E2E testing in a Vue.js application

I’ve been working on a Vue.js application, and I was thinking how I can test it easily. The app have a couple of interesting libraries with custom directives and widgets, some of them with scoped-slot. Additionally I have 10-12 Vue components with hierarchical structure in differents .vue files, wich means I need to test features that (as usual) implies > 1 component. And in this case, I have 4 stores.

My first approach was using Jest, it is pretty easy to install and configure. I tried with 2 small and simpliest tests in the beginning and the result was good. But when I started to think to test larger units with 2 or more components, adding scoped-slots + store + custom libraries with custom components +… each tests was incredibly big and unmantainable. Everything for a really small application with in 10-12 components. It doesn’t makes sense to me. And an important issue: scoped-slot only can be emulated, but you can’t add those to the tests.

Second and great option was create E2E tests with Nightwatch.js. I installed it and configure locally and with BrowserStack pretty fast which allow me test on my Firefox and Chrome local installation but most important run my tests on different OS (like Windows and MacOS) and Browsers (like Chrome, Firefox, Safari), but most important I could create tests really easy. Let me show quickly how you can configure it locally on my laptop (Ubuntu 19.04) (installation process is available in docs):

In my root path I have this:

In test folder I have my tests, in conf my nightwatch config files (for BrowserStack and local), in output I have the JUnit XML files (those are results of Selenium tests). If some of the tests doesn’t result as expected, an screenshot is saved in screenshots folder. And finally in log folder there are log files with issues about tests and selenium server results.

Let’s see how looks my local nightwatch settings:

module.exports = {
    src_folders: ["e2e_tests/tests"],
    selenium: {
        launch_url: process.env.HOST,
        start_process: true,
        server_path: require('selenium-server').path,
        cli_args: {
            'webdriver.gecko.driver': require('geckodriver').path,
            'webdriver.chrome.driver': require('chromedriver').path
        },
        log_path: './e2e_tests/log'
    },
    test_settings: {
        default: {
            launch_url: process.env.HOST_TEST,
            screenshots : {
                enabled : true,
                on_failure : true,
                on_error : false,
                path : "./e2e_tests/screenshots"
            },
            output_folder: "./e2e_tests/output"
        },
        chrome: {
            desiredCapabilities : {
                browserName : 'chrome',
                alwaysMatch: {
                    acceptInsecureCerts: true
                },
                chromeOptions: {
                    w3c: false
                }
            },
            webdriver: {
                port: 4444,
                start_process: true,
                server_path: require('chromedriver').path,
                log_path: './e2e_tests/log'
            }
        },
        firefox: {
            desiredCapabilities : {
                browserName : 'firefox',
                alwaysMatch: {
                    acceptInsecureCerts: true
                }
            },
            webdriver: {
                start_process: true,
                port: 4444,
                server_path: require('geckodriver').path,
                log_path: './e2e_tests/log'
            }
        }
    }
}

This settings will open my local Chrome and Firefox browsers and run tests in port 4444.
Just to test the app quickly I can create a simple test in e2e_tests/test folder like:

// e2e_tests/tests/FirstTest.js
module.exports = {
    'Checking title must contains Hello World! string in HTML title' : function (browser) {
        browser
            .url(process.env.HOST)
            .waitForElementVisible('body')
            .assert.titleContains('Hello World!')
            .end();
    }
};

If I add this script in my package.json
"e2e": "./node_modules/.bin/nightwatch e2e_tests/tests/ -c ./e2e_tests/conf/nightwatch.conf.js"
I can run my first test on Chrome and Firefox running: $ npm run e2e -- --env firefox,chrome .
And tatan!! after open both browsers simultaneously and then close automatically, I got something like:

firefox [App] Test Suite
firefox ================
firefox - Connecting to localhost on port 4444…
firefox
firefox ℹ Connected to localhost on port 4444 (7131ms).
firefox Using: firefox (77.0.1) on linux 5.3.0-59-generic platform.
firefox Results for: Checking title must contains Hello World! string
firefox ✔ Element was visible after 142 milliseconds.
firefox ✔ Testing if the page title contains 'Hello World!' (10ms)
firefox ✔ firefox [App] Checking title must contains Hello World! string (5.429s)
firefox [Filter] Test Suite
firefox ===================

chrome [App] Test Suite
chrome ================
chrome - Connecting to localhost on port 4444…
chrome
chrome ℹ Connected to localhost on port 4444 (2494ms).
chrome Using: chrome (83.0.4103.116) on Linux platform.
chrome Results for: Checking title must contains Hello World! string
chrome ✔ Element was visible after 58 milliseconds.
chrome ✔ Testing if the page title contains 'Hello World!' (12ms)
chrome ✔ chrome [App] Checking title must contains Hello World! string (1.944s)
chrome [Filter] Test Suite
chrome ===================

OK. 2 total assertions passed (6.023s)

Now let’s see how looks my nightwatch config file for BrowserStack, so we can run our tests in multiple browsers and OS remotely:

// e2e_tests/conf/browserstack.nightwatch.conf.js

nightwatch_config = {
    src_folders: ["e2e_tests/tests"],
    selenium: {
        start_process: false,
        host: "hub-cloud.browserstack.com",
        port: 80,
        log_path: './e2e_tests/log',
        output_folder: "./e2e_tests/output"
    },
    test_settings: {
        default: {
            launch_url: process.env.HOST,
            screenshots : {
                enabled : true,
                on_failure : true,
                on_error : false,
                path : "./e2e_tests/screenshots"
            },
            log_path: './e2e_tests/log',
            output_folder: "./e2e_tests/output"
        },
        chrome_win10: {
            desiredCapabilities : {
                'browserstack.user': process.env.BROWSERSTACK_USER,
                'browserstack.key': process.env.BROWSERSTACK_ACCESS_KEY,
                'os': 'Windows',
                'os_version': '10',
                'browser': 'Chrome',
                'browser_version': (process.env.CHROME_W10_VERSION) ? process.env.CHROME_W10_VERSION : '83.0',
                'resolution': '1280x800'
            },
            webdriver: {
                log_path: './e2e_tests/log'
            },
            log_path: './e2e_tests/log',
            output_folder: "./e2e_tests/output"
        },
        chrome_catalina: {
            desiredCapabilities: {
                'browserstack.user': process.env.BROWSERSTACK_USER,
                'browserstack.key': process.env.BROWSERSTACK_ACCESS_KEY,
                'os': 'OS X',
                'os_version': 'Catalina',
                'browser': 'Chrome',
                'browser_version': (process.env.CHROME_CATALINA_VERSION) ? process.env.CHROME_CATALINA_VERSION :  '83.0',
                'resolution': '1280x800'
            },
            webdriver: {
                log_path: './e2e_tests/log'
            },
            log_path: './e2e_tests/log',
            output_folder: "./e2e_tests/output"
        },
        firefox_win10: {
            desiredCapabilities : {
                'browserstack.user': process.env.BROWSERSTACK_USER,
                'browserstack.key': process.env.BROWSERSTACK_ACCESS_KEY,
                'os': 'Windows',
                'os_version': '10',
                'browser': 'Firefox',
                'browser_version': (process.env.FIREFOX_W10_VERSION) ? process.env.FIREFOX_W10_VERSION : '77.0',
                'resolution': '1280x800'
            },
            webdriver: {
                log_path: './e2e_tests/log'
            },
            log_path: './e2e_tests/log',
            output_folder: "./e2e_tests/output"
        },
        firefox_catalina: {
            desiredCapabilities : {
                'browserstack.user': process.env.BROWSERSTACK_USER,
                'browserstack.key': process.env.BROWSERSTACK_ACCESS_KEY,
                'os': 'OS X',
                'os_version': 'Catalina',
                'browser': 'Firefox',
                'browser_version': (process.env.FIREFOX_CATALINA_VERSION) ? process.env.FIREFOX_CATALINA_VERSION : '77.0',
                'resolution': '1280x800'
            },
            webdriver: {
                log_path: './e2e_tests/log'
            },
            log_path: './e2e_tests/log',
            output_folder: "./e2e_tests/output"
        },
        safari_catalina: {
            desiredCapabilities: {
                'browserstack.user': process.env.BROWSERSTACK_USER,
                'browserstack.key': process.env.BROWSERSTACK_ACCESS_KEY,
                'os': 'OS X',
                'os_version': 'Catalina',
                'browser': 'Safari',
                'browser_version': (process.env.SAFARI_CATALINA_VERSION) ? process.env.SAFARI_CATALINA_VERSION : '13.0',
                'resolution': '1280x800'
            },
            webdriver: {
                log_path: './e2e_tests/log'
            },
            log_path: './e2e_tests/log',
            output_folder: "./e2e_tests/output"
        }
    }
};

// Code to copy seleniumhost/port into test settings
for(var i in nightwatch_config.test_settings){
    var config = nightwatch_config.test_settings[i];
    config['selenium_host'] = nightwatch_config.selenium.host;
    config['selenium_port'] = nightwatch_config.selenium.port;
}

module.exports = nightwatch_config;

In this file config will run my tests in BrowserStack on Chrome and Firefox in Windows10 and Catalina OS (Mac) and Safari on Catalina OS. Results of those tests will be in e2e_tests/output, but in BrowserStack App Live Dashboard as well with a nice Video and Screenshots. Awesome, eh?

In my package.json I added other script for BrowserStack tests: "e2e:browserstack": "node ./e2e_tests/conf/local.runner.js -c ./e2e_tests/conf/browserstack.nightwatch.conf.js"
So now I can run my tests just running: $ npm run e2e:browserstack -- --env firefox_win10,safari_catalina
I will get in my console something similar to:

Connecting local
Connected. Now testing…
firefox_win10 Using insecure HTTP connection on port 80. Consider using SSL by setting port to 443 in your Nightwatch configuration.
firefox_win10 [App] Test Suite
firefox_win10 ================
firefox_win10 - Connecting to hub-cloud.browserstack.com on port 80…
firefox_win10
firefox_win10 ℹ Connected to hub-cloud.browserstack.com on port 80 (10779ms).
firefox_win10 Using: firefox (77.0) on windows 10.0 platform.
firefox_win10 Results for: Checking title must contains Hello World! string
firefox_win10 ✔ Element was visible after 1999 milliseconds.
firefox_win10 ✔ Testing if the page title contains 'Hello World!' (418ms)
firefox_win10 ✔ firefox_win10 [App] Checking title must contains Hello World
! string (7.624s)
firefox_win10 Using insecure HTTP connection on port 80. Consider using SSL by setting port to 443 in your Nightwatch configuration.

In my BrowserStack App Live build for Firefox on Windows 10 I will see something like:

So finally, now I just need to add more tests and add it to my CI/CD pipeline.

Aaaaaaaaaand, today we have a super clasic Argentine musician who wrote incredible rock music since 80’s. One of him masterpiece is «El amor después del amor» (it’s Love after love).

 

Emigrate to Canada as a Developer

With the objective to help and inspire to families interested in emigrating to Canada as workers I recently talked about this issues through YouTube Live. It is in my native language: Spanish. You can use the YouTube translation feature.

Without any more preambles here the video:

That’s it, enjoy!

HEY HEY wait… music! This time I have something special. A few days ago a co-worker introduced me an incredible trumpeter: Ibrahim Maalouf! I never heard nothing similar… enjoy the trip:

 

Vue: watch a store object/array property

A few days ago using Vuex I tried to use watcher vue’s property to apply some changes in a specific vue’s component according to a store property. It looks easy to do, just adding to my component:

watch: {
    '$store.state.myStore.aProperty': function(newValue) {
        // do something
    }
}


If this.$store.state.myStore.aProperty it is not an object or array it should work perfectly. But in my case I would like to watch an array of objects. So this solution would not work, that’s because we should use deep property it’s something like:

watch: {
'$store.state.myStore.aProperty': {
deep: true,
handler(newValue): {
// do something
}
}
}

To my surprise this doesn’t work either, looking on internet (including in my favorite lifeguard: StackOverflow), I couldn’t find the solution easly. In fact I had to think about the issue and find my solution on my own, that’s because I’m writting it. Let’s see how looks my store and what’s the change I’m interested to watch:

let myStore = {
    aProperty: {}
};
export default {
    state: myStore,
    mutations: {
        setAProperty: (state, newValue) => {
          state.aProperty[newValue.key]= [];  
          state.aProperty[newValue.key].push(newValue.value)
        }
    }
}

I’m using a new key for my store object and as a value an array where I’m using the push method to add a new value.

Looks like push doesn’t dispatch the correct event to watch vue’s feature. But if I use assign values it works, it’s like: state.aProperty = otherObject it works. So I use a auxiliar variable to save a copy of the current store and re-assign it to the store object in order to dispatch the correct event for watch feature:

let myStore = {
    aProperty: {}
};
export default {
    state: myStore,
    mutations: {
        setAProperty: (state, newValue) => {
            let auxVar = Object.assign({}, state.aProperty);
            auxVar[newValue] = [];
            auxVar[newValue].push(newValue.value); // Using push in auxiliar var
            state.aProperty = auxVar; // This line will dispatch the correct event
        }
    }
}

I hope this is useful to you!


And now as usually let’s learn something interesting on music. Spinetta-Jade was a jazz / rock progressive Argentine band, with incredible exploration between Tango/Symphony rock/Jazz with high virtuosity in fretless bass (by Pedro Aznar), guitar (by Luis A. Spinetta) and other incredible musicians. I love this because it represents the exploration of popular 80’s Argentine rockers in Progressive rock and jazz, since in USA and England this stage was on the 70’s, in this case we add folk, tango and other influences that makes it a precious jewel in the Argentine culture.

 

Moving to Canada

Almost 3 years ago I haven’t written anything here! I am alive and I exist!

A few months ago I received an email from a Spanish company with an invitation to be part of a selection process to work as a developer. I never thought about the idea to move to another country for work, much less in Spain. After that experience, a few months later I received a LinkedIn messenge form a german IT recruiter for a job in Germany. That made me think I could work and move to another country.

Well, there was anything to stop me from doing it, so I started to think about it… but I guess for reasons of time and work I just abandoned the idea. Until a good day, I received an email from an Argentine recruiter living in Auckland and he had an interesting offer to work as a Sofware Develper in New Zeland. I remember that moment was like a sign that I should pay more attention about work in another country. At the same time my wife and I started to think about start a family.

If you are a Developer and think about your family, security and future, Latin America is not a good option at the moment, trust me. The question is, where could be an acceptable option? After research and try to understand what we want for our family and what is important for my career, we found Canada sounds like the best option for us.

I mean it is not just about the country, the culture, the safe place, etc. It’s about my career as well. I will spent most of my time with my colleagues, my teammates working for a product or service that I need to trust, in a company with nice values and culture aligned to my expectations, I should like this and stay motivated. So, I spent some time time thinking and discussing with my wife about our future, the objectives in my career, my next steps as a Software Developer, you know it is a really great and important change for our lifes and for my career.

The question are: Are there opportunities as a Developer in Canada? How to move to Canada if you are a Developer? What I need to move to Canada as a Developer? and there are a lot of other questions that I had around this. I sent five or six resumes to a different companies via LinkedIn without success. Fortunately I found VanHack a great company that makes a great job bringing Canadian (and European) companies closer to qualified candidates trough a modern platform and system. Try it, it’s free! it will save you a lot of time and resources trying to answer the questions I mentioned if you are thinking about work in Canada. If you spend time and effort you will get your job in Canada more easier and in less time.

Thanks to VanHack and the effort dedicated to make a plan and a hard preparation now I’m working for Pressbook an amazing Canadian company, remotely at first but ready to move to Montreal for work when visa is ready with increible teammates highly qualified and motivated creating and developing a super interesating open source product that allow hosts and mantain book production networks for academic institutions, presses and consortia. Once more time I feel I’m working for real and interesting purposes, and on the other hand with the incredible opportunity for me and my family to move to Canada soon!

Thanks Pressbooks, VanHack and God for this opportunity of a new life for me and my family.

Do you think you’ve heard tehcnically complex music? I believed that yes, I mean there are incredible genius… but let me show you something:

 

 

MavensManager: features for Poker Mavens

Mavens Manager

I developed a new product for Poker Mavens of Briggsoft Works: MavensManager.

MavensManager is a backend for owners of Poker Rooms with Poker Mavens developed with CakePHP 3.xx (API) and AngularJS (Front End). Jackpot, Rake in realtime, Stats and an Affiliate system are some of the features.

Each user can view all the reports and histories of the game, and get stats for know trends of their business.

For more info send me a message to: iam[_at_]ricardoaragon.com

Remembering good music.

 

Cut the middleman in Programmatic Advertising

Taggify just launched a new platform that allow get your own bidder within a few minutes! You can get 1 to N bidders and distribute your endpoints selecting the location based on supply and latency.

Other great features: Up to 12000 QPS per instance and with Real-Time access to metrics!

Bamboo is a conductive pipe with no intermediaries, programmatic media buying through Real Time Bidding!

More in: http://bamboo.taggify.net

 

Using AngularJS on CakePHP 3

In this last week I have been working in a new project  using CakePHP 3 , MySQL and other tools when MongoDB and the framework AngularJS. I worked with CakePHP in the version 2 and have found some new nice improvements.

The first sorprise is the new ORM, in this version Cake separe the Tables of the Entities. The tables objects (as the older «Models» objects) allow make CRUD operations with database data, define relations, etc. But the Entities allow define the behavior and functionality of the an instance.

For some integrate AngularJs in CakePHP 3, I used a structure as follow:


webroot/
---------/js
------------/MyApp <-- Name of your app
------------------/controllers //<-- your controllers here
------------------/directives // <-- your directives here
------------------/lang // <-- translate files (ngTranslate)
------------------/modules // <-- your modules here
------------------/services // <-- your services/factories heres
------------------/app.js // <-- define the app AngularJs here
------------------/angular.min.js // <-- Angular file

After you must add in the html tag of your default layout (usually in CakePHP 3: scr/Template/Layout/default.ctp):

<html  data-ng-app="MyApp" id="ng-app">

Create a new folder called «Element» and a file «angular.ctp»:

<?php 
 //echo $this->Html->script('MyApp/lang/translates.en', array('inline' => false));

 //Angularjs Inclusion
 echo $this->Html->script('angular.min.js', array('inline' => false));
 
 //print the modules that are dependencies for our app 
 echo $this->Html->script('MyApp/modules/ngTranslate', array('inline' => false));
// echo $this->Html->script('MyApp/modules/ui-bootstrap-0.11.0.min', array('inline' => false));
 echo $this->Html->script('MyApp/modules/ui-bootstrap-tpls-0.13.0.min', array('inline' => false));
echo $this->Html->script('MyApp', array('inline' => false));
?>

And include this in default.ctp (or in default layout):

<?php echo $this->element('includes/angularjs'); ?>

With this configuration, you are ready for write new controllers, models, directives and services for your application. For example for this project, I writed a functionality for an message system:

<div class="modal-content">
   <div class="modal-header">
       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
       <h4 class="modal-title" id="actionsModalLabel"><?php echo __('Write a message'); ?></h4>
   </div>
   <div class="modal-body">
      <form class="form-group" role="form">
         <select class="form-control" ng-model="DashboardCtrl.recipient">
            <option ng-repeat="affiliate in DashboardCtrl.affiliates" value="{{affiliate.id}}">{{affiliate.user.name}}</option>
         </select>
      </form>
   </div>
   <div class="modal-footer">
      <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
      <button type="button" class="btn btn-primary">Save changes</button>
   </div>
</div>

In the main section I added the ngController:

<div class="row" ng-controller="DashboardController as DashboardCtrl">

 

I will continue writing about this project. The next thing to explain is how to integrate MongoDB 🙂

Now… you know… MUSIC!

 

Leading a team

There are many ways for lead a development team. Some of them require a work of long term, and other are more practical. The long term is not viable, because the members of a team in this sector is changing constantly.

I know three ways for lead a team that is succesfull if we combine them:

Leader with direct orders
table-militar-startupThis dictatorial way can be used when a project is in crisis, about to fall. In this case the team need a decisive leader and with personality. Also is helpful when must be a make radical changes. Sometimes the members resist to radical changes, or to new labor policies.

If you have member without compromise or enthusiasm you would up the team with this way. But this not good to long term, because some people can be desmotivated.

The coach leader

coaching-leaderThe coach leader is a technical referent that cooperates with all the team. He take desicions together with all members. Using this method, you should share the knowledge and generate feedback constantly . Each step is explained and discussed with the workteam, empathy and enthusiasm and promoting the personal growth.

The risk of this is the low productivity because the under control.

 

The director

director-leaderWorking of this mode the leader shows the way, directs the activities and monitors all process but doesn’t participating directly. Supports all their trust in your team. This is useful in teams with high degree of motivation and compromise. In short: direct and delegate.

The great disadvantage of this is that team feel that their leader is not actively part of the team and no commitment as equals.

You must combine this ways, depending of the circumstances. Anyway, be honest, listen of your team, delegate and respect regardless of the technique used, is the most important… for me 🙂

 

Before starting a new application for Start Up projects

Many times is thought that when starting a new start up project, we must be quick in gather the requirements, quick in development process, quick in test process… etc. We must devote specially attention and time to the requirement process. This step is the more important, and this step is decisive for the success or failure of the project.

I thought some fast tips for starting a new start up project:

  • Gather information about the customer company:
    • Objectives, services, products. Is a USP? (Unique Selling Proposition).
    • Competitors.
    • Define the targeted audience for the new application (age, social sector, etc).
  • Access to the application (devices, resolutions, etc)
  • What likes/dislikes of the other applications.
  • Define the limit for the plan project: final date, resources

checking list start up

This few points seem fast, but it is a round trip with the customer of many meetings before starting the new project. In each meeting the customer will define new features and flows, and the original idea will mutate.
Mutate a started application can be very difficult, or not viable, for that reason define this points before of start is the best way of avoid the failure.

Define first… take five 🙂