Automated WooCommerce Testing with Ghost Inspector

WooCommerce sites are made up of a complex set of integrated parts. There’s WordPress, WooCommerce itself, other third-party plugins, and a theme. Each of these components require frequent updates and has the potential to break critical functionality on your site. This is why it’s critical to have automated tests.

For a WooCommerce site I used to work with, we had a checklist of items we would manually run through after any major update:

  • Verify products on home page look correct and load
  • Test “Add to Cart” button
  • Test removing item from cart
  • Verify all product on /shop page look correct
  • Test complete checkout with Stripe for guest checkout
  • Test complete checkout with PayPal for guest checkout
  • Test complete checkout with Stripe with coupon for guest checkout
  • etc.

Needless to say, this took a lot of time. Thankfully, tests like this can all be automated using Ghost Inspector.

Continue reading

How to Set JSON Endpoints in WordPress to Access an External API

There’s a lot of JSON-based APIs that only provide access through server-side methods. If you want to use client side javascript with one of these external APIs, you’ll need to have your server access the data and serve it through ajax requests or your own JSON endpoint. Thankfully, this is really easy to do with WordPress.

In this example project, I’ll show how to get shipping rates from the Easy Post API with a custom WordPress endpoint. Continue reading

Multiple Domains, Tracking and Third-Party Cookies

Tracking anonymous visitors across multiple domains is difficult because cookies can’t be shared across domains. One way around this is to use an iframe to set the cookie, which works well in Chrome, Firefox, and Edge. However, Safari prevents a challenge because it prevents third-party cookies from being set.

I haven’t found a solution for the Safari issue yet, but thought I’d share the method I’ve worked out for cross domain tracking with Mixpanel for the remaining browsers.

For the example, I’ll use domains a A.com and B.com. A.com is the primary website and B.com is a marketing website that refers traffic to A.com.

On domain A.com you’ll need to set up an URL that loads the tracking code when it’s called inside of an iframe. We can pass tracking data from B.com to the iframe using a query string, and an efficient method for passing that data is by base64 encoding an JSON object. Continue reading

Working with the Mixpanel API

We use Mixpanel at Cratejoy to track a lot of user interactions across the sites. However, there was a lot of profile data we were storing (and paying for) that we weren’t actually doing much with. So, I decided to see if it was possible to back up this data locally, create re-import files (in case we ever needed it again), and then delete in bulk.

Here’s some notes and tips about that process. Continue reading

How to Overwrite a Theme

If you’re trying to install a theme update manually, you might get this message:

Installing the themeā€¦
Destination folder already exists.
Theme install failed.

In order to update the theme, you’ll actually need delete the current version before you can upload the new one.

Here’s how to do that:

  • Go to “Appearance > Themes”
  • Temporarily switch to an alternate theme
  • Then, click “Theme Details” for the theme you are trying to replace
  • Click the “Delete” link in the bottom right
  • Now click the “Add New” button and upload the new theme version
  • Switch to the new version of the theme you uploaded

Limit Search Form to Specific Post Types

If you’d like to limit the search form on WordPress site to specific post types globally, there’s and easy way to do that:

// Change search function globally to search only post, page and portfolio post types
function prefix_limit_post_types_in_search( $query ) {
if ( $query->is_search ) {
$query->set( 'post_type', array( 'post','page', 'portfolio' ) );
}
return $query;
}
add_filter( 'pre_get_posts', 'prefix_limit_post_types_in_search' );

But let’s say you want to have a specific search form search limited to one post type, but allow other search forms to search all post types?

Most tutorials I’ve run across suggest that you re-create all the search form markup and then add a hidden input to the form like this:

/* Hidden input field that can be added to search form */
<input type="hidden" name="post_type" value="portfolio">

You can then conditionally load that hidden field on the specific templates where where it is needed.

However, if you’re using get_search_form() and don’t want to completely replace all the search form markup, an alternative would be to do a string replace before outputting the form:

<?php
// Output a search for that only searches portfolio post types
$search = get_search_form( false );
$find = '</form>';
$replace = '<input type="hidden" name="post_type" value="portfolio">' . $find;
$search = str_replace( $find, $replace, $search );
echo $search;
?>

For more reading on how this filter works, you can read the great inline docs in the WordPress codebase itself.

Programmatically Update Settings in Staging

When developing WordPress sites I generally have three environments: live, staging, and local. Since I like my staging environment to be a very close replica of production, I frequently overwrite the database and files in staging. This is especially true when working with a host like WP Engine that has one-click staging environments.

However, when the database is overwritten in staging, there’s a generally a few settings that still need to be different from production. For instance, with WooCommerce sites, I may need to deactivate SSL and put Stripe into testing mode.

Occasionally I’ll also need to deactivate certain analytics plugins or third-party API integrations like MailChimp.

After making these updates manually for months, I finally moved to a programmatic update routine for many of my sites. The code basically just checks which environment we’re in. If it’s not production, it checks if the settings have been updated yet. If not, it makes the updates. Continue reading