Selenium : Hiding Login Credentials In An Automated Test

With most web automation and testing tasks you are going to want to login to a secure system.   You are also going to want to keep your login credentials out of the testing code repository.    For my test suite that is being rewritten with Selenium for the MySLP service I am employing a basic JavaScript module methodology along with some git ignore rules to keep my credentials private.

In this example I have also added the ability to switch between production and staging environments and load the proper credentials for each.   This can be done by setting an environment variable on my operating system before running the test.   In my case I can do this in my phpStorm execution instructions or from the MacOS command line if I choose to run my JavaScript app from there.

The Premise

In my setup I have a config subdirectory which is in the public repository that configures the variables I need that are specific to my environment.  This holds things like my URLS to get to the apps , a list of test users, and even the admin login credentials sourced from another place.

The other place for admin credentials is the env subdirectory.  The public repo has a *-example.js.  The mirror of these are the *-real.js set of files which are kept out of the repo.  It is these -real.js files that are read during the program execution.

Any of the devs on the team can copy production-example.js to production-real.js and replace the username and password with their own.   They can do the same for staging.

By default the testing is run against staging.   The QA team can switch environments to test the production servers by setting the environment variable NODE_ENV before executing the tests.  Setting it to production or staging accordingly.

The Code

The test script.

 

The configuration lib.

 

The example environment lib.

The ignore file

 

How It Works

Loading The Environment

Assigning the require of the myslp_config lib to a local myslp_config variable makes it globally accessible within the running test.   All of the exported properties from the module become properties of the myslp_config variable so things like myslp_config.url_for points to the URLs in the myslp_config file.    We can then reference myslp_config.url_for.dashboard to get to the dashboard interface or myslp_config.url_for.my to get to the main marketing interface (short for the MySLP site or to be more specific the MySLP marketing site).

 

The myslp_config file itself will look at the NODE_ENV environment variable if set and internally set the environment property to ‘staging’ or ‘production’.   The default, if the NODE_ENV variable is not set is ‘staging’.   We then use this to further refine our url_for variable to set the proper server paths.

 

Define The Test Actions

The myslp_dash_login function runs through the automated web driver, in my case for Safari, and runs the steps to type in the admin username and password and then click submit.  Using the string of .then() methods from the Promise JavaScript construct we can ensure the username and password are typed before clicking submit.

 

Execute The Test

Using the standard Selenium driver init and webDriver commands we launch the test and when done hold the window open for 10 seconds before closing it.

Details about setting up Selenium with NodeJS and automating Safari are in my other articles.

Selenium Web App Test: Are My Web Parts There?

As per my previous articles, I am building new test cases using Selenium as a replacement for my older Selenium IDE tests.  Selenium IDE is no longer supported by Firefox 55+ and the next generation doesn’t appear as though it will be ready any time soon.  If you are going to continue testing your web apps with Selenium, now is the time to learn Webdriver.

This is a continuation of the Selenium automated web testing series.   You can  use Selenium to automate testing of your web apps for quality assurance or to automate routine data entry.    It is a great way to write “code bots” to make them do all the repetitive tasks for your app.

My Environment

I’ve setup my MacOS box to run automated testing with Safari which is driven by a Node.js app.     Since automation is built into Safari since version 10 this makes the simpler than having to find/build/install browser drivers for things like Chrome, Firefox, or IE however once you do so the remaining script coding should be the same.

You’ll want to find my prior article on setting up the environment to ensure you have Node.js installed with the relevant Selenium drivers.     In my test cases I have a directory with a webdriver_tests sudirectory where my test scripts live and a node_module subdirectory where node has installed all the libraries for running the node app.   I also run my testing through phpStorm as I prefer the smart completion and live syntax checking when writing code.   You can run your tests from any JavaScript aware IDE or from the command line using the node command to run your script.

Test Objective

In this test I want to open my target website URL and check to see critical elements are present.    I will also interact with one of the elements and to prepare further testing where I will later build an automated new user sign up test module.

In this case our MySLP service has 4 different offerings that behind-the-scenes have been assigned the green, yellow, blue, and orange service levels.    We will test that all 4 offerings have their respective divs renedered on the home page with a primary button the user can click to sign up.    Later tests we can add things like “make sure this text label is present” but we’ll leave that out for now.

MySLP Home Page 2017-09-24_12-47-40.png

In case you are wondering, abstrating by colors allows us to later change the name of the services without having to change lots of back end code or testing scripts.

The Script

As before I will start with the entire script so you can see high-level view then I’ll drill down into various components and shares notes on what they do.     You may also want to keep tabs on this as I’ve been finding that despite the strength of the testing tool itself, the Selenium documentation as a whole is horrific.   You almost need a phd in computer science just to figure out how to use it effectively.

Side Note: One of the many “good luck figuring this out” documentation examples, finding what the options are on the .forBrowser() call.    It simply says “The name of the target browser; common defaults are available on the webdriver.Browser enum” and leaves you to figure it out.    The Browser enum lives in the capabilities.js module of the selenium-webdriver lib of node.    

forBrowser enum.png

Here is the enum at the time of writing this article:

The Setup and Execution

The first part of the script and “launch” part of the script have been covered in previous articles.  We setup the configuration and use the seleniumDrivers.init() to launch it when everything is ready.  I won’t get into the details here.

The Home Content Test

The first test we run is to check the home page content is there.    It opens the web page with the Selenium Driver get() method and strings along a series of FindElement() methods to test things are as we expect them.

I am using the By.xpath() method to locate elements by their xpath.  This allows me to string along HTML dependencies like “find the div for the orange class that is INSIDE the div with the price-tables class”.  This is helpful when HTML elements that need to be tested do not have unique classes or IDs.

What is with all the .then() calls?

You’ll want to read a about the Promise construct in JavaScript.  It allows us to launch commands and let them finish “whenever they are done”.  Like finding a page element.       .then() is a construct that says “when you are done go do this next”.     By stringing together a bunch of .then() methods we can basically turn a bunch of “find this then that” commands that finish at random times into a “do this, then do that, then do that” ordered series that behaves more like a sequential (synchronous) app.

Side note: Many things in JavaScript run asynchronously.  That means stuff can happen in random order. Think of it like sending 10 people to run off and buy stuff for a party, they all leave at the same time but you have no clue who is coming back first; the guy with the burgers, the gal with the buns, or the dude with the chips.    

Why make it synchronous?

For the findElement() testing I don’t really need to test they are all there in sequence.  In the grander scheme of things I do want to know if ALL the tests in a group passed or failed.   In order to do this you’ll see that I’m seeting a boolean in the my_utils object to track if any of the tests failed.     If I were to test this after a series of independent findElement() calls it is 99.9% likely that none (and certainly not all) of the findElement() tests will have finished their work before the JavaScript engine evaluates that boolean.  Setting up a list of tests to run they testing a boolean is FAR faster (some 1,000+ times) than scanning an HTML DOM.

If you don’t believe me, comment out the last .then() test with my_utils.report_on_all_tests( ‘Home Content’ ) and move my_utils.report_on_all_tests( ‘Home Content’ ) outside the closing driver.get() call.   It will run before any of the tests begin to execute.

The Signup Test

The first test, myslp_home_content(), checks our key elements are there.    The second test looks for the ‘orange button’ and clicks it.  For now the test is simplified and only clicks the button.  It will be extended for element checking and interaction to automate the sign up process later.

Here I am showing how to interact with an element by using the .then() processor to test for 2 cases.  This is slightly different than the .then() construct used to string together tests in sequence.   Here I am using the default parameters of .then() which is a function to run when the promise has been fulfilled (success) and when it has not (failed).

If you break it down we find an element by using its xpath.   We then call the anonymous function which is passed the object we were looking for and execute the click() command on that object.    If it fails we call the my_utils.log_missing_item method which logs an error and sets a flag we can later test to report back if the entire test suite worked with the my_utils.report_all_tests() method.

In the initial code example above you may notice I intentionally left a typo in the code looking for class “pt-buttonx” instead of “pt-button”.  This is to show the output of a failed test.    Fixing this will execute the button click and the test will carry on.

Selenium Simple Web Element Testing.png

Side note:  One last hint before we wrap up this example, you can learn a bit more about how Selenium Webdriver works with JavaScript by finding the “code easter egg”.  Hidden in the selenium-webdriver node library is an example directory.   In it you’ll find, not surprisingly, examples of some basic tests.     You may even learn some new tricks in there.  It is how I learned to easily string together tests and make them behave in sequence as per my example here.

Selnium Examples .png

Selenium IDE Extensions Hacking

I use Selenium IDE as a tool for testing the Store Locator Plus WordPress plugin.   It is a great tool for automating browser interactions and sussing out basic problems.  With the launch of the My Store Locator Plus SaaS service we need to build more complex tests for multiple accounts and services.    Thankfully Selenium IDE not only has a myriad of plugins but makes it easy to create your own.

While “officially sanctioned” Selenium Plugins are true Firefox browser plugins you can deploy your own commands written using the Selenium IDE objects and interfaces.   These can be included by listing your local JavaScript file in the core extensions file list.

Now for the bad news.   The documentation for building extensions is horrid, outdated, and typically non-existent.   Which is where this article comes in.   This is my personal notes on how to hackify your own Selenium IDE extensions.

You can create your Selenium Extension as any other JavaScript program.    When you’ve created it you can add it via the Selenium Options menu drop down under core menu extensions.

Selenium.prototype.do<command>

This is the JavaScript command that you use to define a new Selenium IDE command.   If needs to point to a function that is passed 2 parameters.  The first parameter is what the user put in the target field.   The second parameter is what the user put in the value field.

This would add a new command you invoke with MyCommand in Selenium IDE.

The command definition in the JavaScript prototype must start with an uppercase.

The command in the SCRIPT FILE will start with a lowercase.

this.continueFromRow( <int> )

Run the Selenium IDE command in the current test from the specified entry.

throw new Error( <string> )

Generate an error that will be logged by Selenium.

Selenium Variables – storedVars

Any store commands will place the variables in a named index in storedVars[<name>] where name is the value you assigned during the store.

testCase

The Selenium object that contains details about the current test being run.

testCase.debugContext.debugIndex

Get the current debug index.    In theory the currently executing row.

testCase.commands

Contains the array of all the commands for the current test case.

testCase.commands[<int>].type

Type of command.    At least one valid type is ‘command‘.

testCase.commands[<int>].command

The command the user entered, first entry in a Selenium row.

 

More Info

You can find some of my hacks based on the Sideflow Selenium plugin on Go With The Flow over at Bitbucket.

%d bloggers like this: