Author Archives: Nathanael Anderson

NativeScript 2.5.0 - Released

Some of you might have seen the all New version 2.5.0 has been released today.  For the first time that I can recall; Telerik has actually beat me to the release news.   You can read the official blog post here.

Some of the new features

  • Better Debugging using Chrome Developer tools
  • Working Webpack 2.0
  • Flexbox layout fixes
  • Updated Android Runtime Engine (even more ES6 support)
  • More css support for ActionBar
  • Lots of bug fixes

Upgrading (Core):

First of all to upgrade is done is a couple steps:
> npm install -g nativescript@latest
> npm install tns-core-modules@latest --save

Next try the new update command
> tns update

For Android:
> tns platform remove android
> tns platform add android

For iOS
> tns platform remove ios
> tns platform add ios

Then you can type tns info and verify that everything says v2.5.x

Some Changes

  1. tns run does not work the same way anymore; it is now equal to tns livesync --watch
  2. To actually "rebuild" the app; use tns run android --clean

Common Issues:

  1. In some cases when doing a tns platfrom add android, the package.json file gets a entry in the dependencies section for "tns-android": "^2.5.0" which will cause any following builds to fail with the error code: the plugin tns-android is already installed.   Fix: Delete it out the package.json.
  2. ActionBar items -- backgroundColor do not use color names; only use Hex values.  Using color names can cause the app to crash.
  3. Android --release apps and error about can't find package "nativescript-snapshot@x.y.z" or "nativescript-angular-snapshot@x.y.z" in the registry.     A couple things can be causing this; first make sure you have updated everything.   Second, occasionally the hooks folder gets out of sync, you might have to delete your hooks & node_modules folder and do a npm i to reinstall everything.

 

NativeScript Android Snapshots

For those who haven't deployed any apps in v2.4 of NativeScript; one of the new features that is turned on by default is SnapShots.    Now most the time this is a AWESOME thing, however occasionally this can cause issues.   For example I have one app of mine that this crashes at startup when using SnapShots.

Now the docs do list how to disable snapshots; but it is a lot easier for me to find the notes on my own site than trying to figure out which doc has the info.

The environmental variable you need to adjust is: <strong>TNS_ANDROID_SNAPSHOT</strong>

  • 0 = Force Snapshots off always
  • 1 = Force snapshots on (including in debug mode)
  • Unset = Snapshots only in Release mode

Allowing TypeScript to understand NativeScripts ~/ home path

I know a wide number of you use TypeScript; well one of the irritations I've had with TypeScript -- I just figured out how to solve.   Finally did some research and tests to figure out how to make TypeScript support using ~/ as a normal path for building and determining editor intellisense since this is a special path in NativeScript meaning the home app path.   Using this path in a import / require statement means you can do something like this.

/app/views/login/login.ts ->

import * as animation from '~/support/animation'
and it will load in the file at   /app/support/animation

You can use relative paths, but I find absolute path's a lot easier to read and understand exactly which file is being loaded.   In addition things like my NativeScript-Updater can't use relative path's (do to some low level issues in the iOS runtimes) and determine if a file has been updated.

Ok, so the solution: open your tsconfig.json file and add the following:

"baseUrl": ".",
"paths": {
   "~/*": [
     "./*"
   ]
  }

To the "compilerOptions" key in the json file.

Amazon Tablets - Jailbreaking / Rooting

Amazon sales some decent little tablets called the Amazon Fire 7", add one of there decent kids cases and the tablet is great for letting the kids use for a ereader, comics, games, notes, Zork, and many other tasks that a normal Android compatible touch screen devices can do. All for the grand total of $50 for the android device, and $20 for the case.   So around $70 you have unit that is perfect for kids.

Now with this unit there is a couple of small gotcha; by default it comes with a silly advertising screen saver and it is tied completely to the Amazon eco-system.   For those who aren't interested in Advertising there is two options; one you can pay Amazon $15 to eliminate the advertising, or you can remove it yourself.   Now many of you know me; So I'm sure you can guess which way I did it. 🙂

The second issue is that it only has a 3 month warranty; they DO sell a "kids" version for $99 that comes with the case, removal of the ads and a two year warranty.   So the cost difference between the two is negligible if you want a two year warranty, this is your better bet and you don't have to do anything to eliminate the ads as it comes already without ads.

This device is not a bad little tablet; and my kids fight sometimes over who gets which one of the them that we have.


Now on to why most of you are probably viewing this blog post; I prefer to have full control over any device that enters my house.   So guess what my choice was.  Yep, I eliminated the ads and "root"ed the device.   If that sounds scary; it really isn't.   You just do a couple steps; install some software and boom your done and have a fully open Android tablet.  Please note this can void your warranty and you have a chance you can mess up your device.   No warranty is provided and I don't provide free tech support.  😉 Please note this only works on Amazon Kindle fire 5.3.1 and lower, if you are running 5.3.2; you need to downgrade to 5.3.1 before doing this. There are videos to help you do this.

Please note; by doing this you might eliminate the ability for Amazon's Alexa, freeplace, prime videos, kindle free books from working.   I do have a prime account; but I don't have anything on Amazon I want the kids to have "easy" access too; so I honestly haven't tried any of the prime services on my fire's.   I'm pretty sure the Kindle e-reader app continues to work fine; but no guarantees.    I don't believe my fire's are babysitters, so no video access is given to them.  If they want to be entertained they can read a book.  😉

First things first; you need the tools.  You need to download the link on here:

http://rootjunkysdl.com/files/?dir=Amazon%20Fire%205th%20gen/SuperTool

The file is currently called AmazonFire5thGenSuperTool.zip and it is around 152MB's

After you download it and extract it to a folder; you need to install the ADB drivers; the best tutorial I've seen if you like video's is the author of this tool; "Root Junky" video tutorials which you can watch on http://www.rootjunky.com/amazon-fire-5th-gen-supertool/

If you prefer text and pictures http://www.makeuseof.com/tag/install-google-play-remove-lockscreen-ads-amazon-fire-tablet-without-root/

Now I'm not going to repeat those tutorials; The things I did to make everything work was option 1. ADB Driver Install, then 2. Install Google Play and remove ads from Lock Screen, then 7. Root your Amazon Fire fifth gen, and finally 8. Replace Amazon Fire Launcher with Nova Laucher.
1. ADB Driver Install -- This is required so that the software on your computer can talk to the Amazon Fire, so this has to be done FIRST, and must be working, for everything else to work.  This can be tricky depending on the OS; if you are using Windows 8 or 10; you have to disable driver signing; which you can see how to do in the Text and pictures link.

2. Install Google Play / Remove ads -- well I don't like ads, and I want Google play to be able to install other things, that I mention below.

7. Root your amazon fire; this is if you want to be able to control things; I happen to like putting a firewall on my devices.  This allows me to block all applications from connecting to the internet and dialing home and/or pulling down ads inside of them.    This is not needed; but I personally prefer it.  The firewall I use is called "AFWall+" and can be got for free on the Google Play store. (But you must be rooted to use it).   There are a couple other tools I use on my devices that require root; but I'm a developer so they probably won't interest you.  😉

8. Replace the Amazon Fire launcher with Nova Launcher.  Nova Launcher is a pretty good default launcher; if you like the Amazon launcher; then no need to do this.  But I don't and I find it very limiting and it is very much tied to the Amazon eco-system.  So I install Nova Launcher; then I installed another app called "Kids Place" this allows you to setup a simple to use Launcher for the kids; where I can pick the apps they can run, when they are allowed to be ran and other "kid" friendly items. This "Kids" place runs in its own sandbox and limits the kids to pretty much only the apps you allow it. It has some "holes" that my crafty kids have discovered, but the holes are pretty minor and don't cause any real issues since the firewall blocks access from any app that I've decided doesn't need internet access.

I tend to disable the internet on the tablet, pre-install a ton of books, apps, comics and other items. I have also purchased a License for Moon Reader (My favorite e-reader app), and Aldiko Premium. And so I install these along with FBReader on all my Fire's so that the kids can choose which e-reader they like.  In addition I typically install a couple learning games (for the non-readers), drawing, and even a comic book reader; as everyone loves comics.

If you want to allow your kids access to the internet from the device; I would install the Brave browser as it has built in ability to eliminate ads and tracking which is what you want for your kids. The other thing you might consider is changing the DNS to use a family/kid friendly provider. You can do it on the device; or if you have a smart router; you can tell your router to server a kid friendly dns to a specific device. OpenDNS & Norton both provide free kid friendly dns servers.

None of this is totally bulletproof and you still have to be a parent; but it does make my life a lot easier and I have no concerns about any of the kids taking any of the fires in their rooms and using them.

 

MySQL SSL required connection Ubuntu solutions

I went to implement MySQL replication for a client this evening and ran into some interesting issues that I haven't ran into before. Guess it has been a while since I had to set it up from a client.   So this post is for notes for me or someone else who might need to do this in the future. The normal installation replication installation works great but if you are going to enable ssl connections this is where the things can get a bit more complex.

The first thing to find out is if you have your SSL setup correct, try doing:
And verify the SSL is enabled and build in, in my case everything looked good:

mysql> SHOW VARIABLES LIKE '%ssl%';

have_openssl  = YES
have_ssl      = YES
ssl_ca        = /etc/mysql/certs/ca-cert.pem
ssl_capath    =
ssl_cert      = /etc/mysql/certs/server-cert.pem
ssl_cipher    =
ssl_crl       =
ssl_crlpath   =
ssl_key       = /etc/mysql/certs/server-key.pem\";

This looks correct, so the next thing to figure out is where the error log file is located;
mysql> SHOW VARIABLES LIKE '%error_log%';

log_error     =  ./mysql-bin.err
or something like
log_error     = /var/log/mysql/error.log

Now that you know where the error log is at you can see why it is failing.

In my case the error was this:

2016-12-06 23:21:33 32695 [Warning] Failed to setup SSL
2016-12-06 23:21:33 32695 [Warning] SSL error: SSL_CTX_set_default_verify_paths failed

I love that it is a "Warning".   It is totally broken, but we will list it as a Warning...

Well, this can be caused by several things:

  1. No permissions to the files in the folder, use chmod/chown to give perms.
  2. SELinux blocking it, disable selinux or grant permissions via SELinux
  3. AppArmor blocking it.  (this was my case)

Edit the /etc/apparmor.d/usr.sbin.mysqld file.

You'll see something like this in the file:

/etc/mysql/*.pem r,
/etc/mysql/conf.d/ r,
/etc/mysql/conf.d/* r,
/etc/mysql/*.cnf r,
---> /etc/mysql/certs/*.pem r,  <---
/usr/lib/mysql/plugin/ r,

Add the ---> line <---, make sure it matches your path to where you are storing the certs.  Then restart mysql. After restarting the server, I then got this error: SSL error: Unable to get private key from '/etc/mysql/certs/server-key.pem' 2016-12-06 23:53:32 21728 [Warning] Failed to setup SSL 2016-12-06 23:53:32 21728 [Warning] SSL error: Unable to get private key Ok, this one threw me for a while.  The files are fully readable by MySQL.  The issue ends up being incompatibilities between SSL libraries in use.  OpenSSL 1.0x vs yaSSL The key file will start like this:
-----BEGIN PRIVATE KEY-----
If you used OpenSSL to generate the keys;  OpenSSL creates keys in PKCS#8 with a SHA256 digest.  Of course yaSSL which is (normally) used by MySQL doesn't support either, and want PKCS#1.  So despite having the files fully readable, MySQL is telling you it can't figure out how to "get the private key" out of the file.  Once you know the issue, it has a simple solution:
openssl rsa -in server-key.pem -out server-key.pem
when you are done with this command the beginning of the file should look like this:
-----BEGIN RSA PRIVATE KEY-----
Again, the internal format is different, so don't try and just change the text and insert the "RSA" into it -- it will look like it works until something try's to connect using SSL. Once you have this done, restart mysql again and you should be good to go.

NativeScript 2.4.0 - New Features

ns-version240Some of you might have seen the all New version 2.4.0 has been released today.   This has been a release that has taken a bit of time to get right, but it is finally out!  Wooo Hoooo!!!

Some of the new features

  • NativeScript Workers
  • Per-Side borders
  • Flexbox layout
  • Android Snapshots on Release build (faster app start time)
  • Added "import" to point to the JS file
  • More pseudo selectors: button now supports: pressed, active and highlighted, and views descendants support disabled.
  • Segmented Bar now has a new css property:  selected-background-color
  • TabView now has new CSS properies: tabTextColor, tabBackgroundColor, selectedTabTextColor, and androidSelectedTabHighlightedColor
  • Some CSS properties now support % sizes:  height, width, margin-left, margin-top, margin-right, margin-bottom, margin.
  • Image Capture allows rotation.
  • Camera module now a plugin (Removes a permission)
  • Brand new Theme!
  • Reduce the size of the Android App
  • Much nicer Android crash screen
  • Faster tns prepare
  • Android has ~ 97% support for pure ES6 code.
  • Can create TypeScript typings automagically on android platform using --androidTypings command line.

Lots and lots of bug fixing in all the repo's.

Upgrading (Core):

First of all to upgrade is done is a couple steps:
> npm install -g nativescript@latest
> npm install tns-core-modules@latest --save

For Android:
> tns platform remove android
> tns platform add android

For iOS
> tns platform remove ios
> tns platform add ios

Then you can type tns info and verify that everything says v2.4.x

Common Issues:

  • iOS failing to build, older projects:  v2.4 requires you to have the Info.plist file in the app_resources/ios folder.    The simplist way to fix this is to create a new project and then replace your app_resources with the new app_resources folder.  If you have any resources you have manually added or any changes to any files make sure you copy them out before you delete the old app_resources folder.  I would highly recommend you do NOT merge them as you might get some weird behavior from the old resources in the old format vs the new resources in the newer layout.
  • iOS requires CocoaPods v1.0 or later.   This is not a NativeScript issue so much as the Cocoapod infrastructure no longer allows anything older than 1.0..
  • Android failing to build with some plugins (like NativeScript-Telerik-Ui). with the error Multiple dex files define Landroid/support/v4/accessibility
  • Android failing with snapshot error, install the nativescript snapshot support via
    npm install nativescript-dev-android-snapshot@latest --save-dev
  • TNS no longer building your TypeScript files or livesync'ing any of your TS files.

 

NativeScript - Professional post series

I've been doing NativeScript for a while; and since I'm a contractor/freelancer; and no specific company pays my salary -- I've decided to start putting some of my cool learned tips into the paid category.   Most of these will only qualify if they took me multiple hours to figure out.  Unfortunately nobody pays me for fixing things when they break...  So, if I can use my hard earned knowledge to save you a vast amount of time, what is it worth to you?
All this knowledge is time tested and can save you a vast amount of diagnostic time on why something doesn't work.   So if you think saving you time is worth it; please feel free to sign up and support me; and I will provide the information you need when you need it...
The different series are going to be available on my Patreon site: https://www.patreon.com/NathanaelA
The first series is; NativeScript Platform Differences


You might get an app running perfectly on one platform, and then wonder why it isn't working on the other.   I have started out with a cool post on several issues you might have between iOS and Android using HTTP/HTTPS.

The second series is: Troubleshooting your NativeScript
I have started this series out on dealing with Upgrading from NativeScript  to 2.4 and issues you might face.
The first post in this series deals with a specific iOS upgrading issue.
The second post in this series deal with a specific Android upgrading issue (Error starts with: Multiple dex files), when using several plugins...
The third series is: Ready to Distribute my app...  What now?
The first post in this series deals with a specific iOS Build issues...
The second post in this series deals with a specific iOS Build configuration information.
Some of these posts will be showing up in the next couple day as I have time to finish them off...
And yes; I will continue to post free useful stuff...   😉

NativeScript - Workers

ns-workersOne of the best new features in the brand new 2.4.0 release of NativeScript is WebWorkers.    For those who have seen me around in the community; you will probably all know how long I have been harassing the NativeScript Core Teams to get this done (Since Aug of 2015).  I even went so far as to create a plugin (NativeScript-WebWorkers) that allowed you to spin up more JS threads but with the major limitation that they didn't have access to the native OS side of things, they were only pure JS.    So getting real 100% NativeScript webworkers this release means I am a VERY happy camper!

The feature is fully cross platform (i.e. works on iOS and Android) and allows you to spin up additional JavaScript engines to do all your heavy lifting needs in the background.    Now obviously, the more you spin up the more memory and cpu you will use; so you want to treat them as a precious resource and only spin up those you need.   Let me re-iterate; use these for only heavy duty processes; each worker is another FULL JS engine, which takes a chunk of memory and cpu to just start and maintain.

They still have full access to the iOS and/or Android runtime just like normal.   The only difference between them and the main thread is that you do not have any valid access to the GUI or GUI elements.   You can attempt to modify the GUI, but you will crash your app as you are not allowed in even in a native app to modify the GUI outside the main thread.  Same rules apply to a NativeScript app.

The NativeScript Core Teams modeled the background threads after the web workers model.  They are created, developed and destroyed the same way as a browser web worker would be.   So lets dig in.

Everything is passed via messages between the workers and the main thread; so lets look at a sample demo:

main file

"use strict";
exports.onNavigatingTo = function(args) {
   let myWorker = new Worker('./myWorker.js');
   myWorker.onmessage = function(msg) {
     console.log("Hi I'm a message from the worker: ", msg.data);
     myWorker.terminate();  // We no longer need the worker around, so kill it.
   };
   myWorker.onerror = function(err); {
      console.log("Opps, something went wrong in the worker", err.message);
   };
   setTimeout(function() { worker.postMessage("a Cool Message"); }, 500);
}

myWorker.js
"use strict";
require("globals");
global.onmessage = function(msg) {
  console.log("Got a message form the main thread!", msg.data);
  postMessage("Worker's cool Message");
  // global.close();  // If ran, this would close the worker from inside.
};

global.onerror = function(err) {
  console.log("We can handle our own errors too", err.message);
};

Now as you can see we have two files; the first file is from the main thread it starts the new worker by doing let myWorker = new Worker([path to worker script]); this is how you start a brand new worker.   The new worker will load that JavaScript file and start it up.  Now there are some gotcha's we are going to cover on the worker side that you will want to know about.

  1. You want to require('globals'); as your first or second line.    If you do NOT require the global module, you will not have access to console, setTimeout , setInterval, and any other function you are used to using globally.   So requiring this function is pretty important for most workers.
  2. When you assign .onmessage (or .onerror if you are using it) you must assign them to the global variable.  The new version of the Android engine is enforces "use strict"; properly and having implied "this" variables is NOT allowed.  So as a habit when assigning something to the global scope; implicitly use global.
  3. All messages have a .data parameter that contains the data you sent from the other side.  When you do a postMessage({cool: "wow", I: "am"}); this will be in msg.data.cool and msg.data.I in the onmessage message.  This might catch you, but is easily fixed.  Please make sure that any objects you send across to the other side is fully serializable (i.e. no recursion, no native gui elements) ; if not it will fail unless you use some third party lib to serialize the recursive structure.
  4. Terminate() or Close() the webworker if you are no longer going to use it.  If you are planning on continuing to use it; then leave it running it is cheaper to leave it running (& not doing anything) than to terminate and restart.
  5. If you get an error message like this: Worker Error: Uncaught TypeError: Cannot read property 'prototype' of undefined this can mean it can't find the worker file that you wanted to load.  Using the tilde to say main app folder '~/path/to/worker' is the easiest way to fix it.  OR it can mean that the file that is required is doing something that is causing the worker to crash.
  6. If you see the error: Uncaught TypeError: global.moduleMerge is not a function The solution is to do a require('globals'); at the top of your worker file.

Once you understand these items, you are ready to rock and create cool background threads to do all your busy work so that your main thread never freezes again...

NativeScript Testing: Developer Days Video

I just noticed my video from my presentation was released.  This was the presentation that I did at #NativeScript Developer Day in Sept of 2016.   The subject was Testing your NativeScript app and dealt with both Unit Testing and End to End testing.

I saw a couple of "um" mistakes.    I "um" need to "um" work on "um" removing some "um"'s.  😉

Here are the questions at the end (since like an idiot, I also totally spaced about repeating them) are:
1. What about testing Angular2 NativeScript apps.
2. What about Code Coverage .

Slides Deck:
http://fluentreports.com/blog/wp-content/uploads/2016/09/NativeScript-Testing.pdf

Demo "Carder" App I wrote and used, includes all the tests:
https://github.com/NathanaelA/carder

Detailed Blog posts about this subject covering the video:
Part 1 - Unit Testing
Part 2 - End to End Testing

NativeScript Testing: End to End

 

keyboardI got to give a talk on NativeScript testing to a great group of people at NativeScript Developer Days. 20160920_190247

Despite my silly technical problems at the beginning, we managed to get back on track and I was able to present all the critical information that was needed to get everyone up and running.  I did manage to miss my last slide; so I will make sure to have all that information at the end of each of these posts.

For those who are interested in doing testing and weren't there; or those who were in the talk, and just want to get a quick refresh, or if you just prefer the information written down; then this set of blog posts is for you.   I plan on doing a multi-part series on this as I want to cover what I did in my talk in the first two blog posts; but then expand the series to show Angular Native unit testing, how to unit test controls, and how to write unit tests for the NativeScript core.

You can view the #NSDevDay video here .  You can download my slide deck from the talk here.  And you can download the entire app that I wrote for my talk, including all the tests from here.

The posts currently planned in this series are:

Now as you might have guess even though you can do a lot of tests via unit testing, there are some limitations to unit testing.  You are typically only testing pieces of the whole.  So you really need to make sure the whole app works and the code you used to tie together all the pieces works.  And this is called End to End testing.

In my book, "Getting Started with NativeScript",  I discuss using Appium, for end to end testing.   Since the point where my book was published, one of the NativeScript engineers Hristov Deshev has actually created a really neat plugin.  It actually wraps up all the same steps that I came up for in my book.  Since it is a plugin, it is way easier to use, since it handles all the configuration and installation for you.  You just type tns install appium and it will install everything you need for end to end tests.   In this case all the tests for Appium will be stored in the root /e2e-tests folder since they don't actually need to be part of the Application itself.  Appium uses Mocha (which is the primary reason why I use mocha for my normal unit tests, I like consistency.).  It also uses Chia for the Asserts; so the tests are created the exact same way as I described in the unit testing post; with only a couple minor  changes.

So lets look at your /e2e-tests/carderAppium.js test that I provided in my sample testing application:

var nsAppium = require("nativescript-dev-appium");

The first thing you will notice is that I don't require any of the code from the app; the end to end testing does not run from inside your app; it is 100% external to your app.  So, you are at this point requiring the Appium setup and control driver library that Hristov wrote to wrap the configuration complexity.

describe("Carder app tests", function () {
    this.timeout(100000);
    var driver;

    before(function () {
        driver = nsAppium.createDriver();
    });

    after(function () {
        return driver
        .quit()
        .finally(function () {
            console.log("Driver quit successfully");
        });
    });

We still use the describe; but we have to actually setup the driver (that controls the device) in the before function.  And we tear it down (or close it down) in the after function.   Then we actually do all of our tests...
it("should find the card title", function () {
        return driver
            .elementByAccessibilityId("message")
            .text().should.become('Back of Card');
});

So in the first test, what we have to do is use the driver we created in the before function; pretty much everything uses the driver.  It is the communication channel to the device being tested.   Then the command we use is .elementByAccessibilityId('message').   This command will search the iOS or Android layout for any element in the UI that has the "message" accessibility id attribute set.   Now, in NativeScript this is actually set using the automationText property.  So if you look at my main-dynamic.xml or main-page.xml file; you will see:
<Label row="1" id="message" automationText="message" text="{{ text }}" class="message" tap="scrollOff" textWrap="true"/>

Then once the driver finds this element, it looks at the .text() value and that value should become "Back of Card".   When the app starts up; the first card chosen is the "Back of Card".  So if my app is actually running properly, this test will succeed.

Lets skip to the last test; as understanding it, will explain all the other tests in between. So lets figure out how it works.   Now first thing to understand is in the carder app; the numbers, letters and pips on the card are actually using a font.  So if you were to switch to Arial as the font, the actual underlying character is a "r" for the hearts pip.  So that is why I have "r" and "q" letter used in the tests...

// 'r' is the hearts pip
var heart = 'r';
        
// Setup our xpath
var xpath =  "//"+nsAppium.xpath("Label")+"[@text='"+heart+"']";

// Lets run our checks
return driver.elementByAccessibilityId("prior")
    .should.eventually.exist
    .tap().tap()
    .elementsByXPath(xpath).should.eventually.have.length(2);

As you might have noticed we do another elementByAccessibilityId("prior") -- we are looking for the prior element which is a button in this case (xml is:
<Button automationText="prior" id="prior" text="Prior" tap="prior"/>).

Then once it exists we tap it twice.  As you can see you can keep stacking commands; so in this case we actually stacked tap twice.  This is important to know, because frequently you will want to do different tests to the same element; or multiple actions to the same element.  You can easily chain them.

Next up, we are using the elementsByXPath which searchs the UI for anything that matches the xpath and returns it.  And finally we check the number of elements found.

Appium allows you to set/get values of fields, emulate taps & gestures, act like typing in on a keyboard, or just act exactly like an end user would, and then you can verify the results.  This allows you to build complex tests for your UI that test the entire "user" exposed functionality.

Now lets go into some specific details on some of these commands that you need to be aware of in Appium and NativeScript testing.   The Appium web driver actually has a ton of different selectors; however since Appium was initially developed for the web, only a couple selectors in the Appium documentation actually work for your NativeScript mobile apps.   The two selectors you can use reliably is element(s)ByAccessibilityId and element(s)ByXpath.  "element" returns only the first element found.  "elements" (note the added 's') returns all elements found.    As discussed earlier, AccessibilityId uses the NativeScript automationText value to find item(s).

XPath actually allows you to drill down into the UI and find specific items that may have a specific hierarchy and/or certain parents.  For example; rather than search for all buttons, you can limit the search to buttons that are inside a GridLayout which is inside a StackLayout area.  However, the biggest downside with xpath is that it expects you to have the actual native android or native ios control type name.   For example; on Android the NativeScript Button class is actually using the android.widget.button.  The native class on iOS it is actually using an UIButton.   Now that makes XPath really, really hard to be cross platform test, doesn't it?     So to solve that issue; I have written a cool wrapper to help with this issue. It allows you to pass in your NativeScript class name and it will, depending on the platform you are testing against, will return the real underlying native component name.   So in this specific test case the xpath was "//" = any level of items, we aren't giving any specific parents (so find this anywhere in the layout).  Then my helper class nsAppium.xpath("Label") will give me the actual underlying UI name of a NativeScript Label component, and then finally "[@text='r']" means that element must have a "r" as the text field value.    In the case of the card it should find, the two pips on the edge of the card which should be a "r".  So this test would pass as long as the prior button worked to bring you to a King of Hearts card...

The next thing you need to be aware of in Appium is that you MUST return the driver results. You will see every one of my Appium tests does a return.   In all reality, the entire chain that we are doing is actually a promise chain.  So for the test to actually run and then pass/fail, the final result of the promise chain must be evaluated.  So ALWAYS return the promise chain, or your tests will say they passed without actually knowing for sure that it actually passed or failed.  This is CRITICALLY important you return the final promise!

The final gotcha in Appium is to know is at the top of the test file, the "this.timeout(100000);" is actually very important.  Appium can take a while to actually startup the DRIVER to communicate with the device/emulator.   And you really do not want the test to timeout (which = failure) before it actually starts running it.  So make sure at least for android, that this is a very large value...

A couple notes; Appium launching the driver can be extremely slow.  You have to wait a while before it actually appears to be doing anything.   Second; If you are using my NativeScript-LiveEdit plugin, the watcher now has a cool ability to be able to launch Appium when you hit the "a" button in the watcher window.

Now all of this can be automated and is highly recommended to be automated in something like local git hooks, or some other CI environment.   That way when you commit a change; Unless you have a beefy machine, I would recommend you set it to run on like every 3-5 commits (depending on how frequently you commit, it might be higher). Because Appium is fairly slow to get the whole test started.  At worst case I would recommend you run a Appium at least once a day, several hours before you go home...

If you need help setting up a automatic testing and/or CI environment or you would like some training, please contact me.

Resources: