Blog

Jon Clausen

August 05, 2016

Spread the word


Share your thoughts

An incredibly powerful feature of ContentBox 3 is the increasing availability of open source themes to jump start your custom development.   For those wishing to contribute back to the community, creating your own theme is as simple as cloning one of our boilerplate themes, creating a new GitHub repo for your customizations, and publishing your theme on Forgebox.  

Inevitably, if your theme becomes popular one, others will want to contribute to the community.  Managing the process of pull requests and releases of your theme is greatly simplified with your own continuous integration pipeline.  Today we're going to look at how to set up Travis CI integration for your theme development, to ensure new commits and pull requests to your branches are fully vetted for quality.

In this case, we'll use the existing Asymmetry theme and set it up for travis testing.   In testing your theme, there are two critical areas we need to ensure are verified:

  1. The asset pipeline - using ColdBox Elixr or standalone Grunt or Gulp compilations.
  2. Your custom theme templates and layouts.

Preparing your Theme for Testing

In the case of the asset pipeline, this is as easy as installing NPM and running your compilation.  For the templates and layouts, you'll need to create a test site which utilizes each of those layouts and templates. First though, let's set up our directory structure:

  1. Create a workbench directory in your theme root (the actual name is up to you, but this is a convention we use regularly at Ortus so we'll use that here). We'll use this directory to store items we may need for the build, such as SQL scripts
  2. Create a tests directory for your suites in the theme root (you can place them anywhere, but the root makes it easy for CommandBox to scaffold them out)
  3. Create a TestProxy.cfc file and place it in the workbench directory, to allow your tests to extend the core application settings. We'll copy this later during the build to where it needs to go.  The contents of this file are simply:
    component extends="Application"{}
  4. Create a Travis build file (.travis.yml) in the root of  your project
  5. Copy your working ContentBox test Application.cfc to workbench/Application.cfc.txt, which will be used to auto-configure your Travis ContentBox build.

Create a Demo Site in ContentBox

The most time-consuming step of this process is creating a ContentBox test site which allows you to demo all of your layouts.  Once you've set up this site, simply export a backup of the database to your workbench folder, which we'll use during the build process going forward.  Don't forget to update this file as you develop new features for your theme.  From the root of your theme, to export a MySQL database, simply run:

mysqldump -u[YOUR USER NAME] -p[YOUR PASSWORD] -d [YOUR DATABASE NAME] > workbench/test-db.sql

Create Your Test Harness

First we'll need to create our runner.cfm and Application.cfc (AKA - Our test harness) within your tests directory.  You can grab a boilerplate copy of those files from any of the Coldbox Modules.  To keep things simple, here are the basic contents of those files:

Application.cfc:

component extends="TestProxy"{
    this.name = "ASYMMETRY-THEME-TESTHARNESS-" & hash( getCurrentTemplatePath() );
    this.sessionManagement = true;
    this.whiteSpaceManagement = "smart";

    // any mappings go here, we create one that points to the root called test.
    this.mappings[ "/tests" ] = getDirectoryFromPath( getCurrentTemplatePath() );
    rootPath = REReplaceNoCase( this.mappings[ "/tests" ], "/tests(\\|/)", "" );
    this.mappings[ "/root" ]   = rootPath;

    COLDBOX_APP_ROOT_PATH = rootPath & "/";
    // The web server mapping to this application. Used for remote purposes or static purposes
    COLDBOX_APP_MAPPING   = "/";
    COLDBOX_CONFIG_FILE      = "config.Coldbox";
    COLDBOX_APP_KEY          = "ASYMMETRY-THEME-TESTHARNESS";

    public boolean function onRequestStart( String targetPage ){
        return true;
    }
}

runner.cfm:

<cfsetting showDebugOutput="false">
<!--- Executes all tests in the 'specs' folder with simple reporter by default --->
<cfparam name="url.reporter"         default="simple">
<cfparam name="url.directory"         default="tests.specs">
<cfparam name="url.recurse"         default="true" type="boolean">
<cfparam name="url.bundles"         default="">
<cfparam name="url.labels"             default="">
<cfparam name="url.reportpath"         default="#expandPath( "/tests/results" )#">
<cfparam name="url.propertiesFilename"     default="TEST.properties">
<cfparam name="url.propertiesSummary"     default="false" type="boolean">

<!--- Include the TestBox HTML Runner --->
<cfinclude template="/testbox/system/runners/HTMLRunner.cfm" >

When preparing tests, you may choose to put all your tests in one file or, optimally, create a test spec for each template or layout.  To keep it simple, we'll use CommandBox to create one test spec for page layouts and one test spec for blogs, which we will use to test those layout groups.  We're using our integration test template, since we'll be running actual events to test our layout:

box coldbox create integration-test handler=BlogLayout directory=workbench/tests/specs/integration/layout
box coldbox create integration-test handler=PageLayout directory=workbench/tests/specs/integration/layout

 

No let's add a simple test for one of our pages.  Open your PageLayoutTest.cfc and replace the boilerplate describe() block with the following:

describe( "Performs tests on the page layout objects", function(){

    beforeEach(function( currentSpec ){
        // Setup as a new ColdBox request for this suite, VERY IMPORTANT. ELSE EVERYTHING LOOKS LIKE THE SAME REQUEST.
        setup();
    });

    it( "Tests the standard page layout", function(){
        URL.pageSlug="mypage";
        var event = execute( "page.index" );
        var rc = event.getCollection();
        expect( rc )
            .toBeStruct()
            .toHaveKey( "pageSlug" );
        expect( rc.pageSlug ).toBe( URL.pageSlug );
        structDelete( URL, "pageSlug" );

    });

});

No that we have at least one test to run, let's set up our Travis build so that we can perform these tests.

Create your Travis Build File

We're going to walk you through the process of some key steps in the build, omitting some of the basics.  If you haven't set up Travis integration previously, use this tutorial to get started.   For a completed build file, see here.

Travis Install Block:

We're going to test our asset pipeline and our layouts, so we'll need both CommandBox and NPM installed, along with all of our dependencies  Please make note of all of code comments in the script below:

install:
  # Commandbox and NPM Installation - Do this before we move our files so we can test compilation
  - sudo apt-get update && sudo apt-get --assume-yes install zip unzip commandbox nodejs npm
  - sudo npm install -g bower

  # Move our working root to a theme directory
  - mkdir asymmetry
  - mv !(asymmetry) asymmetry/
  # Make sure to copy our Git directory
  - mv .git asymmetry/
  # Copy our workbench directory up to the root
  - cp -ar asymmetry/workbench ./
  # Move our TestProxy up to the root
  - mv workbench/TestProxy.cfc ./

  # Dependency Setup
  - box install
  - box install contentbox
  - box install testbox
  # Move our pre-configured App.cfc up to the root of the site, replacing our install version
  - mv workbench/Application.cfc.txt ./Application.cfc
  # Move our theme files in to our ContentBox module
  - mv asymmetry ./modules/contentbox/themes/
  # Install our test database
  - mysql -u root -e 'create database contentbox;'
  # import database
  - mysql -u root contentbox < workbench/test-db.sql

  # Start up Commandbox
  - box server start port=49616 rewritesEnable=true openBrowser=false

If all goes according to plan, your ContentBox server will start up and your index page will be available to confirm your installation is up and running.

Travis Script Blocks:

Travis basically runs until it receives an exit code of greater than zero.  To test our build we simply run all of our standard scripts, using some regex goodness to check the output of our testbox results:

before_script:

  - curl http://localhost:49616/

script: > 
    cd ./modules/contentbox/themes/asymmetry && npm install;
    cd ./modules/contentbox/themes/asymmetry && bower install;
    cd ./modules/contentbox/themes/asymmetry && grunt --completion=bash;
    testResults="echo $(box testbox run runner=http://localhost:49616/workbench/tests/runner.cfm)";
    echo "$testResults";
    if grep -i "\[Failures: [1-9][0-9]\?[0-9]\?\]\|\[Errors: [1-9][0-9]\?[0-9]\?\]\|]*>\|]*>" <<< $testResults;  then exit 1; fi

 

That's it.  Once you publish your .travis.yml file, along with your setup files, you can configure Travis CI to run your builds on pushes to specific branches, pull request, and tags.

Auto Publishing

Remember how we make sure to copy the .git directory along with our module?  We can use this, along with CommandBox to publish new releases of your theme.  We'll save this for another post that discusses the use of SSH keys and different deployment options available.   Happy coding!

Add Your Comment

Recent Entries

Partner with BoxLang and Ortus at Into the Box 2025: Empowering the Future of Modern Software Development!

Partner with BoxLang and Ortus at Into the Box 2025: Empowering the Future of Modern Software Development!

At Ortus Solutions, we’ve always been at the forefront of innovation in the ColdFusion ecosystem. From pioneering modern ColdFusion practices to developing cutting-edge tools and frameworks, we’ve been passionate to help and sup[port the community into shaping the future of web development.That’s why we decided to build BoxLang, our new JVM programming language that not only builds on the strengths of ColdFusion but takes modern software development to the next level.

Maria Jose Herrera
Maria Jose Herrera
December 23, 2024
Why BoxLang When You Have Kotlin, Groovy, Scala, and more…

Why BoxLang When You Have Kotlin, Groovy, Scala, and more…

As we approach a stable release of BoxLang and our continued marketing reaches more folks, many have asked about its purpose. Why create a new language when the JVM ecosystem already includes established languages like Kotlin, Groovy, and Scala, to name a few.

Luis Majano
Luis Majano
December 18, 2024