Instantly share code, notes, and snippets.

@thbar /_readme.md
Last active Nov 20, 2018

Embed
What would you like to do?
Using minitest to regression test your Jekyll static site

Using minitest to regression-test your Jekyll static site

I recently had to upgrade my blog, which involved changes such as:

  • Replacing a sitemap plugin
  • Upgrading from jekyll 2.5.3 to 3.8.4
  • Upgrading from jekyll-assets 0.7.8 to 3.0.11
  • (etc)

The upgrading process was not trivial, and some parts (e.g. RSS, sitemap, or twitter cards tags) are not immediately visible, so I decided to add unit tests on the generated content.

Minitest use nice and lightweight, a perfect fit for what I needed here.

I'm sharing these codebits in hope it will help other developers.

Having this test suite ensures that your static site will age well when you'll upgrade the gems.

Getting started

Add the following gems to your Gemfile:

  • minitest
  • minitest-focus (to easily select a single test)
  • awesome_print (to color print when debugging)

Then create a simple tests.rb file.

Here is mine after a few iterations & with a few comments.

The tests assume JEKYLL_ENV is set to production, to test on the output which is uploaded to production.

require 'minitest/autorun'
require 'minitest/focus'
require 'nokogiri'
require 'ap'

class Tests < MiniTest::Test
  # make sure we're not using files with .html, but instead index.html in a directory, for cloudfront support
  def test_urls
    assert File.exists?('_site/2015/06/04/how-to-reformat-csv-files-with-kiba/index.html')
  end
  
  # verify twitter cards (must use domain name etc)
  def test_meta
    doc = Nokogiri::HTML(IO.read('_site/2018/10/11/getting-feedback-from-a-kiba-etl-job/index.html'))
    data = doc.search('meta[name^="twitter"]').inject({}) do |r, e|
      r[e['name']] = e['content'] ; r
    end
    assert_equal 'summary_large_image', data['twitter:card']
    assert_equal '@thibaut_barrere', data['twitter:site']
    assert_equal 'Get feedback from Kiba ETL jobs', data['twitter:title']
    assert data['twitter:description'].start_with?('Learn how to write Kiba jobs')
    assert_match /\Ahttp:\/\/thibautbarrere\.com\/assets\/programmatic\-feedback\-/, data['twitter:image']
  end

  # ensure the sitemap uses the absolute domain name
  def test_sitemap
    doc = Nokogiri::XML(IO.read('_site/sitemap.xml'))
    url = doc.xpath('/aws:urlset/aws:url/aws:loc', 'aws' => 'http://www.sitemaps.org/schemas/sitemap/0.9').last
    assert_equal 'http://thibautbarrere.com/', url.text
  end
  
  # more full url checking
  def test_tweet_buttons
    expected_url = 'http://thibautbarrere.com/2018/10/11/ruby-kaigi-2018-etl/'
    
    doc = Nokogiri::HTML(IO.read('_site/2018/10/11/ruby-kaigi-2018-etl/index.html'))
    url = doc.search('.twitter-share-button').first['data-url']
    assert_equal expected_url, url
    
    url = doc.search('.tweet').first['href']
    assert_equal "http://twitter.com/share?via=thibaut_barrere&url=#{expected_url}&text=Ruby Kaigi 2018 talk - Kiba ETL data pipelines", url
  end
  
  # I rarely visit the RSS hidden, so test it as well
  def test_rss
    doc = Nokogiri::XML(IO.read('_site/rss.xml'))
    links = doc.xpath('/aws:feed/aws:link', 'aws' => 'http://www.w3.org/2005/Atom')
    assert_equal 2, links.size
    assert_equal({
      'type' => 'application/atom+xml',
      'href' => 'http://thibautbarrere.com/rss.xml',
      'rel' => 'self'
    }, links[0].to_h)
    assert_equal({
      'type' => 'text',
      'href' => 'http://thibautbarrere.com/',
      'rel' => 'alternate'
    }, links[1].to_h)
  end
  
  def test_robots_txt
    doc = IO.read('_site/robots.txt').strip
    assert_equal 'Sitemap: http://thibautbarrere.com/sitemap.xml', doc
  end
end

How to run

You just run bundle exec ruby tests.rb.

How to run at build

Put this in _plugins/tests.rb:

Jekyll::Hooks.register :site, :post_write do |site|
  # NOTE: you may want to raise conditionally here to stop the deploy
  system("bundle exec ruby tests.rb")
end

How to focus on a single test

Add:

require 'minitest/focus'

Then:

focus
def test_this_test
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment