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

Testing Web Apps With Selenium and JavaScript

It turns out I’ve been making the setup of Selenium 2 (Selenium Server + Webdriver) far too complicated.   As noted in my prior article, getting the client side of the equation setup with Safari is as simple as going to the developer menu and selecting “Allow Remote Automation”.

The server side of things is almost as easy.  I’ve opted to build my tests in JavaScript since that is the “way of the web app world” these days and because it will fit in very nicely with my PHP-heavy web apps written for WordPress.    That means I can keep my phpStorm environment intact and use the built-in Node, Grunt, WordPress-aware code highlighting AND run my tests from the IDE.   Very nice.

For this example I am using Safari 11 (released September 2017) and adding a Node.js applet to my PHP project for testing my WordPress plugin.    I’ve setup the project with source in phpStorm already so from here I just need a “home” for running the web tests.

Install Node.js

I’ve already setup Node on my MacOS box, you can do the same by downloading and installing the node package on Node.js.

Setup A Directory

I created a directory from the MacOS terminal called “selenium_for_myslp” where I have been storing my Selenium IDE tests from past testing cycles.   Within this directory I I’ve created a new “webdriver_tests” directory where I will write and execute all of my new Selenium (aka Selenium 2 or Selenium + Webdriver) tests written in JavaScript using node packages.

Install The Nodes

From the command line you will need to install both the selenium-drivers and selenium-webdrivers packages.   Go to your project directory and install those modules.

Since I’ve not defined any package dependencies, licenses, repos, or other “overhead” for a typical Node app I see some notices about that.

npm WARN enoent ENOENT: no such file or directory, open ‘/Users/lancecleveland/Store Locator Plus/selenium_for_myslp/package.json’

npm WARN selenium_for_myslp No description

npm WARN selenium_for_myslp No repository field.

npm WARN selenium_for_myslp No README data

npm WARN selenium_for_myslp No license field.

No worries there, the testing app will run fine.

Create The Test Applet

Now I get into phpStorm and add the new directory , the top-level “selenium_for_myslp” directory in my case, to my project.   I see my old IDE tests, the newly-minted node_modules, my old sample_deployments, and my new webdriver_tests directories.

I’ll create a new “generic” subdirectory and in it add a new JavaScript file.    I’ll call it “test.js” because it is a test.

A straight copy-paste from the selenium-drivers example, then replacing “chrome” with “safari”, and I’m in business.

Right-click in the source window and run the code.

Selenium and Node in phpStorm 2017-09-21_16-24-09.png

If all is well a new Safari window will fire up with a dialogue box about running automated tests.   I let it run and it opens the google site in my test case.

Selenium Driven Google

I’m now ready to fully automate my site scripts using JavaScript and Selenium.

PHP Autoload and Singleton(ish) Model

Once every couple of years I take a month off from hacking away at the Store Locator Plus products and delve into some personal projects.  It is a way to learn some new things and try out new techniques without breaking the locator product.   With the locator being my primary source of income these days it is important to keep that intact while “trying new things”.

Some of the things I’ve been working on this week include Backbone, Bootstrap, React, Underscores, some new REST techniques, and a simplified object model for my internal “who cares about backward compatibility” PHP 7 projects.

One of the things I started playing with was autoload functionality.  Yes, it has been around since PHP 5, but it had a major overhaul in 7 and as such I think it is ready for production use.    Here are some things I learned along the way.

<tl;dr/>

If you employ autoloading classes use a singleton-style model.

Autoloading makes it tempting to litter your code with $obj=new Thing(); $obj->do_it();   and later in another function do a similar operation with $obj2=new Thing(); $obj->do_something_else();.

If you don’t use a singleton-style model you are spewing pointers all over the place and chewing up small chunks of memory.   Those pointers can be a few-hundred bytes to several Kb.  Memory usage is less of a concern these days but  allocation and de-allocation (garbage collection) takes time.    These days performance is a critical design feature of any web or mobile app.

The Self-Managed Loader

In some of my older PHP code I use a self-managed loading mechanism.  Throughout the code I use a require_once( “… class file …”) call.    The PHP file uses the class_exists() function to wrap the entire class as well as a small snippet of code that checks a “global list of instatiated object pointers”.   Before you start going off about “God objects”, that is not  how this architecture works.   The list is an array of object pointers I can test to see if a class is already instantiated and if it is I use that pointer.   The objects do not require the main list to function and can live independently as a truly independent object in most cases. I know with my application architecture that each class that uses that method should only EVER be employed once so I use the “object list” to enforce that.

However I didn’t like all of the require_once() calls littering my code.   It slightly more code clutter and my guess is that checking the file resources table in PHP on every one of those calls is likely far less efficient than checking a symbol table (or PHPs version of one).

And example of my “require registration” code…

private function get_string_default( $key ) {
   global $slplus;
   require_once( SLPLUS_PLUGINDIR . 'include/module/i18n/SLP_Text.php' );
   $text_to_return = $slplus->Text->get_text_string( array( 'option_default', $key ) );
}


if ( ! class_exists( 'SLP_Text' ) ) {
   class SLP_Text extends SLP_Object_With_Objects {
     public function get_text_string( $slug ) { return $slug; }
   }

   global $slplus;
   if ( is_a( $slplus, 'SLPlus' ) ) {
      $slplus->add_object( new SLP_Text() );
   }
}

class SLPlus {
  public function add_object( $object ) {
   if ( ! is_object ( $object ) ) {
      return;
   }
   $key = preg_replace( "/^{$this->class_prefix}/" , '' , get_class( $object ) );
   if ( empty( $key ) ) {
      return;
   }
   $this->objects[ $key ] = array( 'object' => $object );
  }
}

Every single class has the class_exists() test wrapper and the global $slplus with the add_object() call.   Inside the SLPlus class I have some utilities to simplify property names and use a __get() magic method so I can reference $slplus->Text to get the single instantiation of the SLP_Text object.

Autoloading

PHP 5 had an autoload function whenever you tried to instantiate a class but forgot to require the PHP class definition file.  You could write a function that said “go look in this directory and require the source”.    PHP 7 reworked this function and gave it a new name.  It is more intelligent about autoloading, to the point where the older autoload() is deprecated in PHP 7.2.    I’m going to only refer to the new”ish” spl_autoload_register() methods here.   The ramifications on HOW you implement this (the Singleton thing mentioned above) are the same regardless.

As noted, autoloading is a way for PHP to perform a last-ditch effort to keep code from crashing when you reference a class but forgot to include the source explicity in your code.    It is a great way to reduce code clutter.    The simple version is you write a function that takes the class name that is missing and tells PHP how to go about finding where that file might be in your source architecture, then usually requiring it to avoid a crash.

Here is the overview from my new RUIn (Are You In?  or “Ruin”) side project.   This is a base class for a custom WordPress Theme I am building for this project.    It does use the class test only because I need at least one global object to reference for other functionality in my theme.  It will also be helpful for the singleton-style model I will describe later.

In my projects I make sure all of my PHP file names match the class name they contain.  This is a general best practice that many PHP developers employ.  It makes your life a LOT easier as you can do some neat code tricks like you see here.   It also means moder IDEs , including my favorite phpStorm, can be far more intelligent about your code (auto-complete, grunt and npm task managers, syntax checkers, code smell, and a lot of other tools need far less wrangling).   The means I can use something as simple as the following code to instantiate my objects when needed.

if ( ! class_exists( 'RUInTheme' ) ) :
   class RUInTheme {
   
      public function __construct() {
         spl_autoload_register( [ $this , 'auto_load' ] );
      }

      public function auto_load( $class_name ) {
         if ( strpos( $class_name, 'RUInTheme_' ) === FALSE ) return;
         require_once( RUInTheme_DIR . '/inc/module/' . $class_name . '.php' );
      }
   }

   $GLOBALS[ 'RUInTheme' ] = new RUInTheme();

endif;

 

Now anywhere in my code I need a new instantiation of an object I can call $obj = new RUInTheme_Thingy(); and it magically includes the source.   My architecture dictates all modules go in the /inc/module/ directory and start with RUInTheme_ if they are part of this application.     Nice.  No more requires throughout my code.

Here is an extended example that loads up WordPress admin functions only when the user is a logged in user looking at backend admin pages or loads UI functions when someone is viewing a front-end page.   It means that the app is not loading memory up with code that is never going to be executed such as UI features that aren’t used on an admin page and vice-versa.

 

<?php
if ( ! class_exists( 'RUInTheme' ) ) :
   class RUInTheme {
      public $objects;

      public function __construct() {
         require( 'RUInTheme_Object.php' );
         spl_autoload_register( [ $this , 'auto_load' ] );
         $this->add_hooks();
      }

      public function __get( $property ) {
         if ( property_exists( $this, $property ) ) return $this->$property;
         if ( array_key_exists( $property , $this->objects ) ) return $this->objects[ $property ];
      }


      private function add_hooks() {
         if ( is_admin() ) RUInTheme_Admin::get_instance(); // Admin Only Stuff
         if ( ! is_admin() ) RUInTheme_UserInterface::get_instance(); // UI Only Stuff
      }

      public function auto_load( $class_name ) {
         if ( strpos( $class_name, 'RUInTheme_' ) === FALSE ) return;
         require_once( RUInTheme_DIR . '/inc/module/' . $class_name . '.php' );
      }


   }

   $GLOBALS[ 'RUInTheme' ] = new RUInTheme();

endif;

Now to extend my application I only need to add a class and matching file name in the inc/module/ directory and use new ClassName() in my code. Sweet!

The Un-Singletons

Now what about those singletons and the memory issue?

I quickly discovered that having super-clean readable code was cool but I was pissing memory all over the floor and mucking up my server.   That’s fine for my local Vagrant box (thought it was breathing heavy when hammering the app) but on a production server that would not be a nice “feature”.    Here is what I was doing as I didn’t need to reference the object just employ the methods:

/**
 * WP Hooks
 */
private function add_hooks() {
   if ( is_admin() ) new RUInTheme_Admin(); // Admin Only Stuff
   if ( ! is_admin() ) new RUInTheme_UserInterface(); // UI Only Stuff
}

Functionally this was fine.  The constructor setup the proper WordPress hooks and the methods did their magic whenever I needed them to.   Great clean clode.   Code that happens to piss memory all over the floor just for fun.

No, the memory leak was not a huge issue.  A small 320 byte pointer that was never referenced.  In reality the memory was not a leak as the architecture only calls these objects once and they are never re-used.  Whether or not I assigned the new object to a variable the memory consumption would not change.    Not a big deal until you start getting into larger more complex apps.

The problem with larger apps is you will likely end up with a class that has an object that is polymorphic, or has independent properties for each element of a list of objects, or some other recursive deployment.     In a very useless example, you would end up with dozens of 300-700 byte pointers hanging around a give PHP a lot more clean-up work with something like this:

function setup() { $obj = new Theme_Admin(); $obj->setup(); }
function update() { $obj = new Theme_Admin(); $obj->update(); }
function breakdown() { $obj = new Theme_Admin(); $obj->breakdown(); }

While this is an over-simplified example that can be optimized in many ways you can imagine this being part of multiple independent classes such as an AJAX or REST processor that can not be certain the Theme_Admin() object has been invoked and does not have public properties to share the instantiation.    For these to be truly independent objects they each must employ their own pointers to the object.   Luckily PHP 7 is smart enough to handle the object definition itself in shared memory , signficantly reducing memory load and increasing performance, but there is still a lot of excess overhead with this model.

There is a better way.

A Singleton Autoloaded Model

Use a PHP class version of a singleton by employing a static instance property in your class.    Add it to a public method and instead of using a new call to deploy an object you use your instance manager method.     This ensures all of your PHP references that use this model get back the same memory pointer.    Less overhead for PHP overall and with a large or highly recursive application a lot less memory usage.

Since I employ this method for nearly all of my classes in my application I create a base object that each of my auto-loaded classes will extend.    To invoke a new object I use the static call to the class with something like Theme_Admin::get_instance().   Much cleaner than a long require_once() with a path.

Here is the full example:

RUInTheme_Object

<?php
defined( 'ABSPATH' ) || exit;
if ( class_exists( 'RUInTheme_Object' ) ) return;

/**
 * Class RUInTheme_Object
 *
 * A class that some objects will extend when they need to be a singleton connected to the RUInTheme global object.
 */
class RUInTheme_Object {

   /**
    * Return an instance of the object which is also registered to the RUInTheme global less the RUInTheme_ part.
    * @return mixed
    */
   public static function get_instance() {
      static $instance;

      if ( ! isset( $instance ) ) {
         $class = get_called_class();
         $instance = new $class;

         $short_class = str_replace( 'RUInTheme_' , '' , $class );
         $GLOBALS[ 'RUInTheme' ]->objects[ $short_class ] = $instance;
      }

      return $instance;
   }
}

 

RUInTheme

<?php
defined( 'ABSPATH' ) || exit;
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && ! empty( $_POST[ 'action' ] ) && ( $_POST[ 'action' ] === 'heartbeat' ) ) return;

if ( ! class_exists( 'RUInTheme' ) ) :

   /**
    * Class RUInTheme
    *
    * @property        array       $objects        Arrays of instantiated singletons.  The key is the class name less the leading RUInTheme_ part.
    *
    * Auto-registered singletons so our phpStorm autocomplete is smarter...
    *
    * @property-read   RUInTheme_StartARuin_Shortcode  StartARuin_Shortcode
    */
   class RUInTheme {
      public $objects;

      /**
       * RUInTheme constructor.
       */
      public function __construct() {
         require( 'RUInTheme_Object.php' );
         spl_autoload_register( [ $this , 'auto_load' ] );
         $this->add_hooks();
      }

      /**
       * Get our properties, including registered objects.
       *
       * @param $property
       *
       * @return mixed
       */
      public function __get( $property ) {
         if ( property_exists( $this, $property ) ) return $this->$property;
         if ( array_key_exists( $property , $this->objects ) ) return $this->objects[ $property ];
      }

      /**
       * WP Hooks
       */
      private function add_hooks() {
         if ( is_admin() ) RUInTheme_Admin::get_instance(); // Admin Only Stuff
         if ( ! is_admin() ) RUInTheme_UserInterface::get_instance(); // UI Only Stuff
      }

      /**
       * Autoload objects.
       *
       * @param string $class_name
       */
      public function auto_load( $class_name ) {
         if ( strpos( $class_name, 'RUInTheme_' ) === FALSE ) return;
         require_once( RUInTheme_DIR . '/inc/module/' . $class_name . '.php' );
      }


   }

   $GLOBALS[ 'RUInTheme' ] = new RUInTheme();

endif;

RUInTheme_Admin

<?php
defined( 'ABSPATH' ) || exit;

/**
 * Class RUInTheme_Admin
 *
 * Things that are run on admin pages.
 */
class RUInTheme_Admin extends RUInTheme_Object{

   /**
    * RUInTheme_Admin constructor.
    */
   public function __construct() {
      add_action( 'admin_enqueue_scripts' , [ $this , 'give_us_style' ] );
   }

   /**
    * Admin CSS
    */
   public function give_us_style() {
      wp_register_style( 'ruin_admin_style' , RUInTheme_URL . '/assets/sass/wpadmin.min.css' );
      wp_enqueue_style( 'ruin_admin_style' );
   }
}

The Object List

You may have noticed I still have a __get() method in the main class as well as the objects property.    Even with the singleton method and autoloading I find it far easier to reference my instantiated objects from auto-derived properties on the main object.    This way I know that all of my primary utilities can be referenced via $MainObj->ClassNameWithoutBase.

I can also be confident that since I’m using the Singleton style implementation I can also call my get_instance() for the classes I need whenever I am starting a logic path and later reference the methods.

In my example below I do manually define global $RUInTheme and provide a @var code hint.  This is so phpStorm auto-completes all my method calls and type-checks parameters in real-time (far fewer bugs, yay!).  I could get away with cleaner code by referencing $GLOBALS[‘RUInTheme’]-><classname>->method() but I opt for faster development and syntax checking over saving a line of text.

Here is the UI code from RUInTheme that makes use of the Singleton which auto-registers itself on the RUInTheme base object using a shorthand (dropping the RUInTheme_ prefix I use on all classses in this project).

RUInTheme_UserInterface.php

<?php
defined( 'ABSPATH' ) || exit;

/**
 * Class RUInTheme_UserInterface
 *
 * Things that are run on UI pages only.
 */
class RUInTheme_UserInterface extends RUInTheme_Object {

   /**
    * RUInTheme_Admin constructor.
    */
   public function __construct() {
      add_action( 'wp_enqueue_scripts' , [ $this , 'give_us_style' ] );
      add_shortcode( 'start_a_ruin' , [ $this , 'start_a_ruin' ] );
   }

   /**
    * Admin CSS
    */
   public function give_us_style() {
      wp_register_style( 'ruin_ui_style' , RUInTheme_URL . '/assets/sass/master.min.css' );
      wp_enqueue_style( 'ruin_ui_style' );

      wp_enqueue_script( 'ruin_ui_scripts' , RUInTheme_URL . '/assets/scripts/ruin_ui.min.js' , [ 'backbone' ] , RUInTheme_VERSION , true );

      if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
         wp_enqueue_script( 'comment-reply' );
      }
   }

   /**
    * [start_a_ruin] processor.
    *
    * @var RUInTheme   $RUInTheme
    *
    * @return string               The shortcode HTML.
    */
   public function start_a_ruin() {
      RUInTheme_StartARuin_Shortcode::get_instance();
      
      global $RUInTheme;
      return $RUInTheme->StartARuin_Shortcode->get_shortcode_html();
   }
}

RUInTheme_StartARUIn_Shortcode

<?php
defined( 'ABSPATH' ) || exit;

/**
 * Class RUInTheme_StartSomething_Shortcode
 *
 * Render the [start_a_ruin] shortcode.
 */
class RUInTheme_StartARuin_Shortcode extends RUInTheme_Object{

   /**
    * Display the HTML for [start_a_ruin]
    */
   public function get_shortcode_html() {
      $text['start_a_ruin']   = __( 'Create A Group Text' , 'ruin' );
      $text['list_name']      = __( 'Tell people what your list is about. ' , 'ruin' );
      $text['list_name_help'] = __( 'A short description so people know what your list is about. ' , 'ruin' );
      $text['create']         = __( 'Create' , 'ruin' );

      $HTML = <<<HTML
      <div class="container-fluid">
         <div class="row">
         
            <!-- column 1 -->
            <div class="col visible">
                    <button id="start_a_ruin_button" type="submit" class="btn btn-primary">{$text['start_a_ruin']}</button>                               
            </div>
            
            <!-- column 2 -->
            <div class="col start_step_one hidden">
               <form id="start_a_ruin_step_one">
                  <div class="form-group">
                     <input type="text" class="form-control" id="list_name" placeholder="{$text['list_name']}" aria-label="{$text['list_name']}" aria-describedby="list_name_help">                
                     <small id="list_name_help" class="form-text text-muted">{$text['list_name_help']}</small>
                  </div> 
                  <button type="submit" class="btn btn-primary">{$text['create']}</button>                              
               </form>
            </div>          
         </div>
      </div>

      <!-- Backbone View -->
      <div id="list-app">
         <ul class="list-list"></ul>       
      </div>

      <!-- Backbone List Template -->
      <script type="text/template" id="list-item-tmpl">
         <p><a href="/wp-json/ruinbot/<%= id %>"><%= name %></a></p>
         <p>ARN: <i><%= arn %></i></p>
         <button class=""remove">Delete</button>          
      </script>
HTML;

      return $HTML;
   }
}

That is the guts of a fully functional WordPress theme with custom shortcodes, a Backbone processor, and is easily extensible with self-checking code (thanks to phpStorms) and object loading.     It is memory efficient in the new responsive JavaScript app being driven by the WordPress REST API.

No, it is not the perfect model, but it is a smarter model for the architecture I am deploying.    Some of this may even find its way back into Store Locator Plus and some of the other projects I have online ; at least the PHP 5.2 compatible parts for my WordPress projects.   Maybe someday WordPress will bump the requirement to PHP 5.6 and I can make more use of some super-cool PHP tricks.

Some jQuery Foo I Learned While “Leveling Up” This Weekend

I am the first to admit that I got on the JavaScript bandwagon a little late.   I was a bit hesitant because of my work with government projects a half-decade ago.   When you work with the US Government you quickly forget the “best of” when coding web apps and instead use their default protocol of using “the oldest crap possible”.   I would not be surprised if they are still using Internet Explorer 6 as their go-to standard.   For the non-geeks, that is the equivalent of setting the standard vehicle fleet to a Ford Model T.  Sure, it is a car that runs on petroleum but it sure as hell isn’t going to get you and your family ANYWHERE safely.

Just 2 years ago I started adding some JavaScript to my locator web app.    It helped bring my 2013 app up to 2001 web interface standards.  A little.   Then I learned about jQuery, a library of features and functions that does a lot of the heavy lifting for you.   It is like going from sawing your own lumber from trees to going down to the lumber yard and picking up 2x4s to build your home.   Much easier.

The Slightly Newer But Old Way

Then I learned jQuery and many of the pre-built “nice to have frills” come shipped with WordPress Core.  What?!?!  Why do 90% of the plugins and themes, from which I snarf a lot of code to make it look like I know what I’m doing, not know this?    During the past year I’ve been learning a lot of new code tricks from my friends at DevriX and teaching myself more by learning new things like advanced jQuery trickery.

New Themes

So now, way down here after my rambling, are my notes on what I learned about jQuery this weekend where I felt myself “level up” on that particular skill.

As you read these tips you’ll notice that I use jQuery “long form” vs. $ which is common practice. I have a good reason for that; lots of WordPress plugins are poorly written and assign no-conflict mode with the $ shorthand improperly and break my application.  When you have 15,000 installations you tend to do things “long form but less prone to others breaking it”.  When I write jQuery… in my examples you most likely see $ instead in “real world” code.

Cache Your Queries

When you want to work with an element on a page you can use jQuery to help find the element and make your changes.    You tend to see stuff like this:

That is not very efficient. This jQuery( <selector> ) reads the entire web page each time and makes a whole lot of JavaScript code run EVERY TIME it is processed UNLESS <selector> is a JavaScript object instead of a string.   Lots of code running = slower web apps.

Instead make jQuery “cache” the objects that it finds the first time around by assigning the selection to a variable.  The “lots of code” runs once in the example here and in the examples below it will create a subset of elements to look through versus your entire web page stack of elements.

In this mode jQuery reads the entire web page ONCE and stores the matching objects in the_dash.   It then can quickly modify just those elements at requested.

Extend Your Cached Queries

Now that you are caching your queries and making your site visitor’s laptop or mobile device work a lot less , which believe it or not can extend their battery life by a whole microsecond, you can extend those caches without doing the “whole read the page thing” again.

Here is how I used to find the sidebar, modify it, then find all the images in the sidebar and hide them:

Nice short code which is a little easier, maybe, to read, but this is horribly inefficient in the “350px” mode.   In this mode JavaScript is reading the entire web page, seeking the sidebar, and changing it.   Then it goes and reads the entire web page again, finds the sidebar, then reads everything in the sidebar and finds the images and changes them.    That is a lot of JavaScript code executing.  Executing code takes time. Time is money as they say.

And here is the far more efficient version:

In this mode it reads the entire page once, and keeps track of what it found in “help_sidebar”.   It then changes what it found without searching again because jQuery is working on help_sidebar which is an object.  If that were all we were doing with it  this would actually be a bit slower since we take the overhead of storing the object with an assigned memory pointer (variable assignment) as noted above.

However when we do the second “change all the images inside that object” we gain back that lost microsecond one-hundred fold.     The second jQuery(help_images)… that is used to modify the image within no longer has to search the entire web page.

BUT… there was a problem.    How do you add “extended selectors” to the cached jQuery?

Above we had ‘.dashboard-aside-secondary > IMG’  to find our images.  This is MORE than just the ‘.dashboard-aside-secondary‘ that we stored in our cache.    Uggh.

Find() To The Rescue

Luckily jQuery has a number of methods that extend your selectors and help you traverse the DOM.  You can find this under the Traversing jQuery docs page.

find() can take any selector or OBJECT, like the one we have containing our sidebar, and then look for any elements inside of it.   As a jQuery padawan I had only ever seen this used to find stuff within the entire DOM.   Being a slow-learner it never dawned on me that this could be extended to ANY part of the DOM not just the entire DOM.

jQuery(help_sidebar).find(‘IMG’) looks within the sidebar only to find images.  This is far faster than reading the entire page.   It then changes those images within.

Children() Is One Level of Find()

One of the incorrect paths I went down, but is very useful to know, is the use of children() in jQuery.    This finds only the matching elements just one level deep in the object stack.   Since you’ve read this far you are a code geek like me so I know that you understand that most web pages are many levels of nested elements and often you want something “deeper down” where you need your great-great-great grandson to be involved.    However there are plenty of cases where I can utilize children() to impact just the next level of menu divs, for example.

Summary

Truly understanding how jQuery selectors and “caching”works and how to modify those cached selections with the jQuery traversal methods is going to bring the efficiency of my apps up to a whole new level.    It may only save a half-second of processing time per page interaction, but it all adds up when you have 15,000 websites hosting millions of page views every day.

For my fellow code geeks out there I hope you learned something new and I’ve given you a shortcut to reaching the next level of your jQuery skill.s

Sidebar: Why “caching” in quotes? Because this doesn’t seem like caching to me but rather object-passing, but maybe I’m missing something I’ll learn at level 3.

PHP Can (Still) Be The Worst At Object And Memory Management

More than 5 years after I first started delving into the PHP world thanks to client demands and our first foray into WordPress programming, one universal truth remains the same.   PHP is still one of the worst programming languages I’ve worked with when it comes to both object and memory management.    Yes, it has gotten a LOT better.   PHP7 is much faster and generally a superior code and object wrangler than many of its predecessors.

But, 5 years later, even with PHP7 at work, it can be absolutely horrid with memory management.    Case in point:  today I wrapped up nearly TWO DAYS of debugging and profiling a WordPress plugin that has been running on tens-of-thousands of websites without any complaints of memory consumption for 4 years.   But today, on my PHP7 development box, I re-discovered those old lingering issues with PHP.  The culprit?   Trying to access an array index for a named array that did not exist.

Read More

%d bloggers like this: