class SlackPageSpeed

Public Instance Methods

configure() click to toggle source
# File lib/slack_page_speed.rb, line 170
  def configure
    config_file = File.open('configuration.yml', 'w+')
    config_file.write("" "---
##############
## Required ##
##############
slack_url: 'https://hooks.slack.com/services/foo/bar/baz' # Set up an incoming webhook for your Slack account here https://get.slack.help/hc/en-us/articles/115005265063-Incoming-WebHooks-for-Slack#set-up-incoming-webhooks
pagespeed_api_key: 'Get an Google Search API key from https://developers.google.com/webmaster-tools/search-console-api/v1/configure'
webpagetest_api_key: 'Get a WebPageTest API key from https://www.webpagetest.org/getkey.php'
slack_channel: 'Select existing Slack channel to post to'
domain: 'https://www.google.com/' # Change this to the domain you want to test
page_list: # Change these to the pages you want to test on your domain
- 'drive/'
- 'calendar/'

#############
## Default ##
#############
slack_username: 'PageSpeed BOT' # Change this to any name you like
slack_bot_emoji: ':zap:'
improvement_emoji: ':racehorse:'
minimal_change_emoji: ':no_mouth:'
regression_emoji: ':no-entry:'
include_timestamp: true
threshold: '2'
logging: true" "")
  end
get_gps_score(page, strategy, iteration) click to toggle source

Gets google pagespeed score for a 'page'

# File lib/slack_page_speed.rb, line 38
def get_gps_score(page, strategy, iteration)
  response = HTTParty.get("https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=#{@domain + page.gsub('Homepage', '') + '?src=TSTT'}&strategy=#{strategy}&key=#{@pagespeed_api_key}")
  unless response.response.code == '200' && JSON.parse(response.body)['responseCode'] != '400'
    puts response
    raise "Couldn't get Google PageSpeed score for #{page}"
  end
  score                                          = JSON.parse(response.body)['ruleGroups']['SPEED']['score']
  @run_results[iteration][page][strategy.to_sym] = score
end
get_old_score(latest_score, page, score_key) click to toggle source
# File lib/slack_page_speed.rb, line 74
def get_old_score(latest_score, page, score_key)
  threshold          = score_key == :rank ? 0 : @threshold
  comparison_message = ''
  if File.exist?(@history_file) && JSON.parse(File.read(@history_file)).keys.include?(page)
    # We want the pagespeed score to be greater than before,
    # but we want the speed index to be less than.
    compare_operator   = (score_key == :speed_index || score_key == :rank) ? :> : :<
    # Open file that stores the history of the latest scores
    file               = File.open(@history_file, 'r')
    # Find the value of the score of the page we want
    old_score          = JSON.parse(file.read)[page][score_key.to_s]
    # Check if it's within the acceptable threshold, otherwise,
    # check if it's greater than or less than (depending on what type of score it is)
    # and add the relevant emoji
    comparison_message = " (was #{old_score})"
    if latest_score.to_f.between?(old_score.to_f - threshold, old_score.to_f + threshold)
      comparison_message += " #{@minimal_change_emoji}"
    elsif latest_score.to_f.send(compare_operator, old_score.to_f)
      comparison_message += " #{@regression_emoji}"
    else
      comparison_message += " #{@improvement_emoji}"
    end
  end
  comparison_message
end
main() click to toggle source
# File lib/slack_page_speed.rb, line 6
  def main
    configuration_info = YAML.load_file('configuration.yml')
    @slack_post_url    = configuration_info['slack_url']

    HTTParty::Basement.default_options.update(verify: false)
    @pagespeed_api_key    = configuration_info['pagespeed_api_key']
    @webpagetest_api_key  = configuration_info['webpagetest_api_key']
    @slack_post_url       = configuration_info['slack_url']
    @history_file         = './pagespeed_history.txt'
    @slack_channel        = configuration_info['slack_channel']
    @slack_username       = configuration_info['slack_username']
    @slack_bot_emoji      = configuration_info['slack_bot_emoji']
    @improvement_emoji    = configuration_info['improvement_emoji']
    @minimal_change_emoji = configuration_info['minimal_change_emoji']
    @regression_emoji     = configuration_info['regression_emoji']
    @domain               = configuration_info['domain']
    @message              = configuration_info['include_timestamp'] ? "Page performance scores for #{Date.today.strftime('%d/%m/%Y')}. \n" : ''
    @threshold            = configuration_info['threshold'].to_i
    @results              = {}
    @page_list            = configuration_info['page_list']
    @logging              = configuration_info['logging']

    @run_results = [{}, {}, {}]
    @page_list.each do |page|
      search_string  = page.gsub('-', '+').gsub('/', '')
      @results[page] = { search_string: search_string }
      @run_results.each do |run_result|
        run_result[page] = { search_string: search_string }
      end
    end

    # Gets google pagespeed score for a 'page'
    def get_gps_score(page, strategy, iteration)
      response = HTTParty.get("https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=#{@domain + page.gsub('Homepage', '') + '?src=TSTT'}&strategy=#{strategy}&key=#{@pagespeed_api_key}")
      unless response.response.code == '200' && JSON.parse(response.body)['responseCode'] != '400'
        puts response
        raise "Couldn't get Google PageSpeed score for #{page}"
      end
      score                                          = JSON.parse(response.body)['ruleGroups']['SPEED']['score']
      @run_results[iteration][page][strategy.to_sym] = score
    end

    # runs a new WebPageTest test based on the url supplied
    def start_wpt_test(page, iteration)
      response = HTTParty.get("http://www.webpagetest.org/runtest.php?url=#{@domain + page.gsub('Homepage', '') + '?src=TSTT'}&location=Dulles.3G&f=json&fvonly=1&k=#{@webpagetest_api_key}")
      raise "Couldn't get WebPageTest score for #{page}" unless response.response.code == '200'
      @run_results[iteration][page][:results_url] = JSON.parse(response.body)['data']['jsonUrl']
    end

    # waits until the status of the run at 'results_url' is 200 (test finished)
    def wait_for_wpt_results(page, result_url, iteration)
      361.times do
        @response = HTTParty.get(result_url)
        puts "#{JSON.parse(@response.body)['statusText']} for #{page}"
        break if JSON.parse(@response.body)['statusCode'] == 200
        sleep 10
      end
      @run_results[iteration][page][:speed_index] = (JSON.parse(@response.body)['data']['runs']['1']['firstView']['SpeedIndex'].to_f / 1000).round(2)
    end

    # Send a slack message with all the scores
    def slack_notify(message)
      HTTParty.post(@slack_post_url,
                    body:    { 'channel': "##{@slack_channel}", 'icon_emoji': @slack_bot_emoji, 'username': @slack_username, 'text': message }.to_json,
                    headers: { 'Content-Type': 'application/json',
                               'Accept':       'application/json' })
    end

    def get_old_score(latest_score, page, score_key)
      threshold          = score_key == :rank ? 0 : @threshold
      comparison_message = ''
      if File.exist?(@history_file) && JSON.parse(File.read(@history_file)).keys.include?(page)
        # We want the pagespeed score to be greater than before,
        # but we want the speed index to be less than.
        compare_operator   = (score_key == :speed_index || score_key == :rank) ? :> : :<
        # Open file that stores the history of the latest scores
        file               = File.open(@history_file, 'r')
        # Find the value of the score of the page we want
        old_score          = JSON.parse(file.read)[page][score_key.to_s]
        # Check if it's within the acceptable threshold, otherwise,
        # check if it's greater than or less than (depending on what type of score it is)
        # and add the relevant emoji
        comparison_message = " (was #{old_score})"
        if latest_score.to_f.between?(old_score.to_f - threshold, old_score.to_f + threshold)
          comparison_message += " #{@minimal_change_emoji}"
        elsif latest_score.to_f.send(compare_operator, old_score.to_f)
          comparison_message += " #{@regression_emoji}"
        else
          comparison_message += " #{@improvement_emoji}"
        end
      end
      comparison_message
    end

    def write_new_scores
      file = File.open(@history_file, 'w+')

      # Clear the file
      file.truncate(0)
      file.write(@results.to_json)
      file.close
    end

# ==========
# RUN SCRIPT
# ==========

# This kicks off a WebPageTest test for every page in the '@page_list' array
    @page_list.each do |page|
      @run_results.length.times do |iteration|
        start_wpt_test(page, iteration)
      end
    end

# Waiting for WebPageTest results and storing them
    @page_list.reverse_each do |page|
      # This collates the results after all tests have finished
      @run_results.length.times do |iteration|
        wait_for_wpt_results(page, @run_results[iteration][page][:results_url], iteration)
      end

      # Average all the GPS scores
      @run_results.length.times do |iteration|
        get_gps_score(page, 'desktop', iteration)
        get_gps_score(page, 'mobile', iteration)
      end

      # Reset the temporary results array
      @results[page][:speed_index] = []
      @results[page][:desktop]     = []
      @results[page][:mobile]      = []

      # Add all results to an array of results for each numerical result type
      @run_results.each do |result|
        @results[page][:speed_index].push(result[page][:speed_index])
        @results[page][:desktop].push(result[page][:desktop])
        @results[page][:mobile].push(result[page][:mobile])
      end

      # Calculate the mean speed, desktop and mobile scores, and the mode search ranking
      @results[page][:desktop]     = @results[page][:desktop].max_by { |i| @results[page][:desktop].count(i) }
      @results[page][:mobile]      = @results[page][:mobile].max_by { |i| @results[page][:mobile].count(i) }
      @results[page][:speed_index] = (@results[page][:speed_index].inject(:+)/@run_results.length).round(2)

      # Build the slack message
      puts @results if @logging
      @message += "\n*" + page + ':*'
      @message += "\n     desktop score: " + @results[page][:desktop].to_s
      @message += get_old_score(@results[page][:desktop], page, :desktop)
      @message += "\n     mobile score: " + @results[page][:mobile].to_s
      @message += get_old_score(@results[page][:mobile], page, :mobile)
      @message += "\n     SpeedIndex: " + @results[page][:speed_index].to_s
      @message += get_old_score(@results[page][:speed_index].to_f, page, :speed_index) + "\n"
    end

    # Add an '@here' to the slack notification if any score has regressed
    @message += ' <!here> Something is worse than last time!' if @message.include?(@regression_emoji)
    puts @message
    slack_notify(@message)
    # Create the scores history file if this is the first run
    File.new(@history_file, 'w') unless File.exist?(@history_file)
    write_new_scores
  end
slack_notify(message) click to toggle source

Send a slack message with all the scores

# File lib/slack_page_speed.rb, line 67
def slack_notify(message)
  HTTParty.post(@slack_post_url,
                body:    { 'channel': "##{@slack_channel}", 'icon_emoji': @slack_bot_emoji, 'username': @slack_username, 'text': message }.to_json,
                headers: { 'Content-Type': 'application/json',
                           'Accept':       'application/json' })
end
start_wpt_test(page, iteration) click to toggle source

runs a new WebPageTest test based on the url supplied

# File lib/slack_page_speed.rb, line 49
def start_wpt_test(page, iteration)
  response = HTTParty.get("http://www.webpagetest.org/runtest.php?url=#{@domain + page.gsub('Homepage', '') + '?src=TSTT'}&location=Dulles.3G&f=json&fvonly=1&k=#{@webpagetest_api_key}")
  raise "Couldn't get WebPageTest score for #{page}" unless response.response.code == '200'
  @run_results[iteration][page][:results_url] = JSON.parse(response.body)['data']['jsonUrl']
end
wait_for_wpt_results(page, result_url, iteration) click to toggle source

waits until the status of the run at 'results_url' is 200 (test finished)

# File lib/slack_page_speed.rb, line 56
def wait_for_wpt_results(page, result_url, iteration)
  361.times do
    @response = HTTParty.get(result_url)
    puts "#{JSON.parse(@response.body)['statusText']} for #{page}"
    break if JSON.parse(@response.body)['statusCode'] == 200
    sleep 10
  end
  @run_results[iteration][page][:speed_index] = (JSON.parse(@response.body)['data']['runs']['1']['firstView']['SpeedIndex'].to_f / 1000).round(2)
end
write_new_scores() click to toggle source
# File lib/slack_page_speed.rb, line 100
def write_new_scores
  file = File.open(@history_file, 'w+')

  # Clear the file
  file.truncate(0)
  file.write(@results.to_json)
  file.close
end