<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom">

  <title>Mogoblog</title>
  
  <link href="http://mogotest.com/blog" />
  <updated>2010-09-04T10:52:04-04:00</updated>
  <id>http://mogotest.com/</id>
  <author>
    <name>Mogoterra</name>
    <email>team@mogotest.com</email>
  </author>
  
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/Mogoblog" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="mogoblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title>Mogotest: Automated Cross Browser Render Issue Detection</title>
    <link href="http://mogotest.com/blog/2010/08/18/automated-cross-browser-render-issue-detection" />
    <updated>2010-08-18T00:00:00-04:00</updated>
    <id>http://mogotest.com/blog/2010/08/18/automated-cross-browser-render-issue-detection</id>
    <content type="html">&lt;p&gt;We&amp;#8217;ve been rather busy since we launched paid plans for Mogotest just three weeks ago. We&amp;#8217;re very pleased to announce our new automated issue detection feature for discovering cross browser rendering inconsistencies. Whereas we&amp;#8217;ve always spidered your site and presented you with a set of screenshots for each URL, this feature will compare the rendering output from two browsers and detect any major rendering inconsistencies, saving you the time hassle of having to manually compare screenshots by inspection. The results for your entire site are available in a concise report and the comparison views have been enhanced to highlight the rendering differences.&lt;/p&gt;

&lt;p&gt;Some screenshots should help illustrate things:&lt;/p&gt;
&lt;div class='figure'&gt;
  &lt;img src='/images/static/auto-issue-detection/overview.png' /&gt;

  Figure 1: Overview of browser render results.
&lt;/div&gt;
&lt;p&gt;Fig. 1 shows the new overview report when a test job is completed. You have a matrix of results indicating your site&amp;#8217;s browser compatibility at the render level relative to a reference browser. The reference browser is the one used during primary development and should represent the canonical rendering of the site. Here we&amp;#8217;re using Firefox 3.5 on Linux as our reference browser. In this case, when testing the techstars.org site, we see that the /iphone-app/ path is particularly problematic across most browsers and that Internet Explorer 6 has render issues across most paths.&lt;/p&gt;

&lt;p&gt;If you click on a cell, you&amp;#8217;re taken to a diff view of that path showing differences in that browser relative to the reference browser. Fig. 2 illustrates a rendering issue with Chrome 5 on Windows on the /iphone-app/ path.&lt;/p&gt;
&lt;div class='figure'&gt;
  &lt;img src='/images/static/auto-issue-detection/error-diff.png' /&gt;

  Figure 2: Sample error diff between Firefox 3.5 (Linux) and Chrome 5 (Windows).
&lt;/div&gt;
&lt;p&gt;Below the screenshots you can see the list of render issues detected. When you select an error it highlights the element in both the reference and comparison browser views, adding an overlay with the upper-left-hand and lower-right-hand coordinates to aide in debugging. If you decide that the render issue is acceptable, you can simply choose to ignore the issue and you won&amp;#8217;t be bothered with it again.&lt;/p&gt;

&lt;p&gt;The improved reporting has cascaded down to our notification emails, too. Whereas previously you would have to click through to our site to see if there were any rendering issues, we now report problems by browser in the notification email (see Fig. 3). Additionally, we indicate the change in errors from the previous test run to help you better determine if it&amp;#8217;s something you need to look into immediately or if it&amp;#8217;s a problem that can be deferred.&lt;/p&gt;
&lt;div class='figure'&gt;
  &lt;img src='/images/static/auto-issue-detection/email.png' /&gt;

  Figure 3: Enhanced email reporting.
&lt;/div&gt;
&lt;p&gt;The new automated issue detection feature is now available for all paying accounts at no additional cost. If you&amp;#8217;d like to see it in action, try running a &lt;a href='https://mogotest.com/'&gt;free page test&lt;/a&gt;. And since automated issue detection is something that could always be improved, please report any issues you run into and we&amp;#8217;ll do our best to handle them better.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Mogotest Public Beta</title>
    <link href="http://mogotest.com/blog/2010/07/19/mogotest-public-beta" />
    <updated>2010-07-19T00:00:00-04:00</updated>
    <id>http://mogotest.com/blog/2010/07/19/mogotest-public-beta</id>
    <content type="html">&lt;p&gt;Mogotest is now in public beta. This means anyone can now try out a limited version of the service for free. It&amp;#8217;s as simple as entering a website address into the free page test box &lt;a href='http://mogotest.com'&gt;on our homepage&lt;/a&gt;. While these tests won&amp;#8217;t showcase absolutely everything Mogotest has to offer, it&amp;#8217;s a great way to run a quick test and try out the service without having to go through the hassle of creating a user account. We hope you like it!&lt;/p&gt;

&lt;p&gt;Now that we&amp;#8217;re in beta, we&amp;#8217;ll be doing a bit more spit &amp;amp; polish work and then will begin charging. We&amp;#8217;d like to thank all of you that have contributed to the success of our alpha and now beta programs. It&amp;#8217;s been a great five months and Mogotest has evolved considerably with your feedback. Most recently, we&amp;#8217;ve added new features like Chrome browser support and a new &amp;#8220;Problems&amp;#8221; tab for site-level test run notices, while also improving spidering, authentication handling, and general service responsiveness. We simply wouldn&amp;#8217;t have been able to do this without your help.&lt;/p&gt;

&lt;p&gt;So how much will it cost? Our individual user plans will be &lt;em&gt;$45 / month&lt;/em&gt;. We&amp;#8217;ll start charging on &lt;em&gt;July 28, 2010&lt;/em&gt;. If you don&amp;#8217;t wish to upgrade to a paid plan at this time, don&amp;#8217;t worry; you&amp;#8217;ll still have read-only access to all your existing test run data. If at any point you decide you do want to upgrade you&amp;#8217;ll instantly regain the ability to perform test runs, add sites, and modify your test groups.&lt;/p&gt;

&lt;p&gt;As always, please let us know if you have any questions or concerns about our switch to paid plans.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>How to Perform Basic Authentication in Selenium</title>
    <link href="http://mogotest.com/blog/2010/06/23/how-to-perform-basic-auth-in-selenium" />
    <updated>2010-06-23T00:00:00-04:00</updated>
    <id>http://mogotest.com/blog/2010/06/23/how-to-perform-basic-auth-in-selenium</id>
    <content type="html">&lt;p&gt;Performing basic authentication in Selenium 1.x may seem impossible at first glance. Selenium 1.x drives the browser via JavaScript, but is unable to do anything when the basic authentication dialog pops up. One way to get around this is to embed the user credentials in the URL. Traditionally this has worked well, but newer browsers have disabled that functionality due to the security risks inherently involved. Fortunately, there is another way: send over the authentication credentials in the request header.&lt;/p&gt;

&lt;h2 id='selenium_as_a_proxy_server'&gt;Selenium as a Proxy Server&lt;/h2&gt;

&lt;p&gt;In order to customize the request headers in Selenium you must be using it as a proxy server. Unless you use the &lt;code&gt;-avoidProxy&lt;/code&gt; argument when you start up the Selenium server, it will serve as a proxy server bound to localhost (i.e., it is not an open proxy). You also must use a browser launcher that configures the browser to use the Selenium server as an HTTP proxy. I discuss this in more depth in my article on &lt;a href='http://mogotest.com/blog/2010/04/13/how-to-accept-self-signed-ssl-certificates-in-selenium'&gt;how to accept self-signed SSL certificates in Selenium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is a best practice to only perform basic authentication over HTTPS if you&amp;#8217;re accessing publicly accessible pages. If all your work is on an internal network and it&amp;#8217;s a trusted environment, basic authentication over HTTP may be acceptable. Just realize that basic authentication is plain text and easily sniffable. If you&amp;#8217;re connecting to a server with a self-signed certificate you may want to read the aforementioned article on how to do so with Selenium.&lt;/p&gt;

&lt;h2 id='basic_authentication_with_selenium'&gt;Basic Authentication with Selenium&lt;/h2&gt;

&lt;p&gt;It probably seems obvious, but you really need to make sure your credentials work. If they don&amp;#8217;t, the browser is going to pop up a basic authentication dialog and your Selenium browser session is going to be effectively locked-up. An example of how to do so in Ruby can be seen in Example 1.&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
require 'rubygems'
require 'net/http'

# Check credentials via SSL.
http = Net::HTTP.new('www.example.com', 443)
http.use_ssl = true

# Make the request with the basic authentication credentials.
req = Net::HTTP::Get.new('/')
req.basic_auth('my_user', 'my_password')
response = http.request(req)

# 401 is the HTTP Unauthorized status code.
if response.code == 401
  puts "Basic authentication failed"
  exit(-1)
else
  log_in_with_selenium()
end  
&lt;/pre&gt;&lt;div class='caption'&gt;Example 1: Ensuring your basic authentication credentials work (in Ruby).&lt;/div&gt;
&lt;p&gt;Once you&amp;#8217;ve verified that your basic authentication credentials do in fact work, you can proceed to add the credentials to your Selenium request. You do so by adding the &amp;#8220;Authorization&amp;#8221; header with a value of &amp;#8220;Basic &amp;#8221; followed by the base 64 encoded &amp;#8220;username:password&amp;#8221; pair. The browser will cache this value for all requests within the same session, so you only need to authenticate on the first request. Example 2 shows how this looks in Ruby using the selenium-client gem.&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
require 'rubygems'
require 'base64'
require 'selenium/client'

# Note that the start URL is not on the domain that we need to authorize for.
# If you set the start URL there, you may see the authorization dialog before
# you're able to modify the request.
Selenium::Client::Driver.new \
        :host =&gt; 'localhost',
        :port =&gt; 4444,
        :browser =&gt; '*googlechrome',
        :url =&gt; 'http://localhost:4444',
        :timeout_in_seconds =&gt; 180,
        :highlight_located_element =&gt; false

# Add in the basic authentication header.
encoded = Base64.encode64("#{my_username}:#{my_password}").strip
browser.remote_control_command('addCustomRequestHeader',
                              ['Authorization', "Basic #{encoded}"])

# Request the page, fully authorized.
browser.open('http://www.example.com/')
&lt;/pre&gt;&lt;div class='caption'&gt;Example 2: Adding basic authentication credentials to Selenium request (in Ruby).&lt;/div&gt;
&lt;h2 id='conclusion'&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Since basic authentication credentials can be supplied as an HTTP request header, you can perform basic authentication with Selenium with a little bit of work. You need to use the Selenium server as a proxy for your browser requests, configure acceptance of self-signed SSL certificates if appropriate, and verify that the credentials work before issuing your request. Once you do so, you&amp;#8217;ll be able to test sites without worrying about the browser locking up on the authentication dialog. Note that this article only addresses basic authentication (the most popular of the HTTP authentication methods). Digest authentication is a completely different beast and the method laid out in this article will not work for it.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Google Analytics Integration with OAuth</title>
    <link href="http://mogotest.com/blog/2010/05/27/google-analytics-integration-oauth" />
    <updated>2010-05-27T00:00:00-04:00</updated>
    <id>http://mogotest.com/blog/2010/05/27/google-analytics-integration-oauth</id>
    <content type="html">&lt;p&gt;One of the new features we&amp;#8217;ve been working on behind the scenes at Mogotest is a &lt;a href='http://google.com/analytics'&gt;Google Analytics&lt;/a&gt; (GA) integration. By integrating with the Web&amp;#8217;s most popular analytics service, we can help users see how specific browser rendering problems are affecting their bounce rates, conversions, and traffic funnels. The sort of stuff you&amp;#8217;d like to know about as a site owner, right? Unsurprisingly, we also use GA ourselves to monitor our own traffic metrics and have some automated reports that help us measure our day to day progress.&lt;/p&gt;

&lt;p&gt;In late 2008, Google began moving away from their own AuthSub authorization system and started offering &lt;a href='http://code.google.com/apis/gdata/articles/oauth.html'&gt;access to all 
their Data APIs via OAuth&lt;/a&gt;. That&amp;#8217;s A Good Thing&amp;#8482;. But since there doesn&amp;#8217;t seem to be a wealth of information on the &amp;#8216;net regarding the GA integration process, I figured it might be worthwhile to document how we integrated it with a Rails-based application and share it with the community. The &lt;a href='http://oauth.rubyforge.org/'&gt;OAuth Ruby Gem&lt;/a&gt; makes most of this process very simple, but there are a few tricky bits that might trip you up if you haven&amp;#8217;t worked with OAuth and the Google APIs before.&lt;/p&gt;

&lt;p&gt;The first thing you need to do whenever you&amp;#8217;re tying into a system using OAuth is obtain the consumer token and secret from the provider. In this case, the provider is Google and we can retrieve this information by visiting &lt;a href='https://www.google.com/accounts/ManageDomains'&gt;Google's Domain
Management Page&lt;/a&gt;. Unfortunately, this URL isn&amp;#8217;t the easiest to find. I probably never would have found it if it wasn&amp;#8217;t for &lt;a href='http://everburning.com/news/google-analytics-oauth-and-ruby-oh-my/'&gt;Dan
Sinclair's earlier GA + OAuth tutorial&lt;/a&gt;. Here&amp;#8217;s what it looks like:&lt;/p&gt;
&lt;img src='/images/static/google-analytics-integration/google-domain-manager.png' alt='Google Domain Management Page' /&gt;
&lt;p&gt;Anyway, once you&amp;#8217;re on the Domain Manager page, add the domain for your application (the address from which authorization requests will originate). You&amp;#8217;ll need to verify ownership by either adding a meta tag to your website, adding a new page, or modifying a DNS record. Once you&amp;#8217;ve done that Google will give you the OAuth consumer key and consumer secret you need for the next steps.&lt;/p&gt;

&lt;p&gt;NOTE: this verification requirement can be painful if you don&amp;#8217;t have anything in production yet or need a separate set of credentials for testing or development. I&amp;#8217;d suggest pointing DNS for some URL like test.yourdomain.com to a staging server to get it up and running and then changing it post-verification so you have at least two sets of OAuth credentials for the separate environments.&lt;/p&gt;

&lt;p&gt;To authorize access to a specific GA account (like one belonging to one of our users), you need an &lt;strong&gt;access token&lt;/strong&gt;. To get an access token, you first need to create a &lt;strong&gt;request token&lt;/strong&gt;. The workflow for getting a request token using the OAuth gem involves creating a new &lt;code&gt;Consumer&lt;/code&gt; object, passing in the consumer key and consumer secret, along with a few other options, and then calling the &lt;code&gt;get_request_token&lt;/code&gt; method. For convenience, I&amp;#8217;ve wrapped up the process of requesting a new Consumer into a library method, since we&amp;#8217;ll need to do it a couple times. Make sure that the token paths specified are exactly what is shown below in order for this to work with Google:&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
class GoogleAuth
  def self.consumer
    OAuth::Consumer.new(oauth_consumer_key, oauth_consumer_secret, {
                        :site               =&gt; 'https://www.google.com',
                        :request_token_path =&gt; '/accounts/OAuthGetRequestToken',
                        :access_token_path  =&gt; '/accounts/OAuthGetAccessToken',
                        :authorize_path     =&gt; '/accounts/OAuthAuthorizeToken' })
  end
end
&lt;/pre&gt;
&lt;p&gt;Our call to &lt;code&gt;get_request_token&lt;/code&gt; is going to get made inside the &lt;code&gt;new&lt;/code&gt; action of our &lt;code&gt;AuthorizationsController&lt;/code&gt;. We&amp;#8217;ll retrieve the request token, specifying a callback and a scope. The &lt;strong&gt;callback&lt;/strong&gt; is the URL that the user will be directed to after authorizing with Google. The &lt;strong&gt;scope&lt;/strong&gt; is used to specify the Google service that the user is authorizing us to access. Google provides &lt;a href='http://code.google.com/apis/gdata/faq.html#AuthScopes'&gt;many 
different API services&lt;/a&gt; in addition to analytics &amp;#8211; Calendar, Documents, YouTube, etc &amp;#8211; but we want to scope to the Analytics API here. We set the token and secret in the user session because we&amp;#8217;ll need them later when the user returns to the site. Finally, we get the authorization URL from the request token and redirect the user to it. Note that we also encode instructions to return to our callback URL after successful authorization.&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
def new
  @request_token = GoogleAuth.consumer.get_request_token({ :oauth_callback =&gt; callback_account_authorizations_url },
                                                         { :scope =&gt; 'https://www.google.com/analytics/feeds/' })
  session[:request_token] = @request_token.token
  session[:request_secret] = @request_token.secret

  redirect_to("#{@request_token.authorize_url}&amp;amp;oauth_callback=#{CGI.escape(callback_account_authorizations_url)}")
end
&lt;/pre&gt;
&lt;p&gt;From the users&amp;#8217; point of view, they&amp;#8217;ll get a link to click to &amp;#8220;enable Google Analytics&amp;#8221;, which will take them to the &lt;code&gt;new_authorizations_url&lt;/code&gt; corresponding to the action above. This will then auto-redirect them to the Google authorization page, which will resemble the screenshot below:&lt;/p&gt;
&lt;img src='/images/static/google-analytics-integration/google-oauth.png' alt='Google Authorization' /&gt;
&lt;p&gt;After they authorize us to access their data, they&amp;#8217;ll be redirected back to our callback URL. When Google redirects to this URL they&amp;#8217;ll include an important query parameter that we need to create our precious access token: the &lt;strong&gt;OAuth verifier&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s what the callback action should look like in our controller:&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
def callback
  unless session[:request_token] &amp;amp;&amp;amp; session[:request_secret] 
    flash[:error] = "No authentication information was found in the session. Please try again."
    redirect_to(root_url)
    return
  end

  unless params[:oauth_token].blank? || session[:request_token] ==  params[:oauth_token]
    flash[:error] = "Authentication information does not match session information. Please try again."
    redirect_to(root_url)
    return
  end

  @request_token = OAuth::RequestToken.new(GoogleAuth.consumer, session[:request_token], session[:request_secret])
  @access_token = @request_token.get_access_token(:oauth_verifier =&gt; params[:oauth_verifier])
    
  session[:request_token] = nil
  session[:request_secret] = nil

  current_user.update_attributes(:access_token =&gt; @access_token.token, :access_secret =&gt; @access_token.secret)

  flash[:notice] = "You have successfully linked your Google Analytics account."
  redirect_to(account_path)

rescue Net::HTTPServerException =&gt; e
  case e.message
  when '401 "Unauthorized"'
    flash[:error] = "This authentication request is no longer valid. Please try again."
  else
    flash[:error] = "There was a problem trying to authenticate you. Please try again."
  end 

  redirect_to(:action =&gt; 'new')
end
&lt;/pre&gt;
&lt;p&gt;The first two conditional blocks just check to make sure that the token and secret are found in the user session and that the token provided by Google in the parameters matches the one that the user previously established. The next step is creating an access token from the verifier that Google sent back to us. This is the token we&amp;#8217;ll use to access the GA resources for the user going forward.&lt;/p&gt;

&lt;p&gt;We can then clear out the session variables and update the user model with the access token and secret that we&amp;#8217;ve obtained. By storing these values in our database, and associating them with the user account that authorized us, we can rebuild the tokens we need to obtain analytics data from Google at any time, without having to go through this process again.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s the controller code for our &lt;code&gt;AuthorizationsController&lt;/code&gt; in its entirety (including a method to de-authorize us):&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
class AuthorizationsController &amp;lt; ApplicationController
  before_filter :require_user

  def new
    @request_token = GoogleAuth.consumer.get_request_token({ :oauth_callback =&gt; callback_account_authorizations_url },
                                                           { :scope =&gt; 'https://www.google.com/analytics/feeds/' })
    session[:request_token] = @request_token.token
    session[:request_secret] = @request_token.secret

    redirect_to("#{@request_token.authorize_url}&amp;amp;oauth_callback=#{CGI.escape(callback_account_authorizations_url)}")
  end

  def create
    redirect_to(:action =&gt; :callback)
  end

  # oauth callback
  def callback
    unless session[:request_token] &amp;amp;&amp;amp; session[:request_secret] 
      flash[:error] = "No authentication information was found in the session. Please try again."
      redirect_to(root_url)
      return
    end

   unless params[:oauth_token].blank? || session[:request_token] ==  params[:oauth_token]
     flash[:error] = "Authentication information does not match session information. Please try again."
     redirect_to(root_url)
     return
   end

    @request_token = OAuth::RequestToken.new(GoogleAuth.consumer, session[:request_token], session[:request_secret])
    @access_token = @request_token.get_access_token(:oauth_verifier =&gt; params[:oauth_verifier])
    
    session[:request_token] = nil
    session[:request_secret] = nil

    current_user.update_attributes(:access_token =&gt; @access_token.token, :access_secret =&gt; @access_token.secret)

    flash[:notice] = "You have successfully linked your Google Analytics account."
    redirect_to(account_path)

  rescue Net::HTTPServerException =&gt; e
    case e.message
    when '401 "Unauthorized"'
      flash[:error] = "This authentication request is no longer valid. Please try again."
    else
      flash[:error] = "There was a problem trying to authenticate you. Please try again."
    end 

    redirect_to(:action =&gt; 'new')
  end
  
  def destroy
    current_user.update_attributes(:access_token =&gt; nil, :access_secret =&gt; nil)
    flash[:notice] = "You have successfully removed your Google Analytics account link."
    redirect_to(account_url)
  end
end
&lt;/pre&gt;
&lt;p&gt;And example routes:&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
ActionController::Routing::Routes.draw do |map|
  map.resource :authorizations, :collection =&gt; { :callback =&gt; :any }
  …
end
&lt;/pre&gt;
&lt;p&gt;At this point we&amp;#8217;re all set up to use Google Analytics data in our application. So we should probably do something interesting with it. Fortunately, Tony Pitale has made interfacing with GA itself &amp;#8211; that is, once OAuth connection has been established &amp;#8211; insanely easy with his &lt;a href='http://github.com/vigetlabs/garb'&gt;Garb gem&lt;/a&gt;. To use Garb we can simply create a new Garb session object using a previously established OAuth access token (I personally like to wrap this up into a method on the User model):&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
session = Garb::Session.new
session.access_token = OAuth::AccessToken.new(GoogleAuth.consumer, user.access_token, user.access_secret)
&lt;/pre&gt;
&lt;p&gt;And then we can retrieve account and profile data from Garb&amp;#8230;&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
accounts = Garb::Account.all(session)
profile_id = accounts.first.profiles.first.id
&lt;/pre&gt;
&lt;p&gt;In addition to accessing raw account info, stat values, etc, you can create some pretty elaborate &amp;#8220;reports&amp;#8221; in Garb. A sample report might look like:&lt;/p&gt;
&lt;pre class='brush: ruby;'&gt;
class PageViewReport
  extend Garb::Resource

  metrics :unique_pageviews
  dimensions :page_path

  filters { eql(:page_path, '/') }
  filters { eql(:page_path, '/blog') }
end

report = PageViewReport.results(profile_id, :session =&gt; session,:start_date =&gt; 10.days.ago, :end_date =&gt; Time.now)
report[0].page_path
# =&gt; '/blog'
report[0].unique_pageviews
# =&gt; 500
&lt;/pre&gt;
&lt;p&gt;Tony does a much better job explaining &lt;a href='http://wiki.github.com/vigetlabs/garb/building-a-report'&gt;how reports
work&lt;/a&gt;, and what you can do with them. I&amp;#8217;d suggest checking out his &lt;a href='http://wiki.github.com/vigetlabs/garb/'&gt;Wiki&lt;/a&gt;, &lt;a href='http://github.com/vigetlabs/garb'&gt;GitHub repository&lt;/a&gt;, or the series of posts over at &lt;a href='http://www.viget.com/extend/introducing-garb-access-the-google-analytics-data-export-api-with-ruby/'&gt;Viget 
Extend&lt;/a&gt; for more information on Garb.&lt;/p&gt;

&lt;p&gt;To present this data to a user, we can now toss together some pretty graphs and charts using my favorite JavaScript graphing library, &lt;a href='http://g.raphaeljs.com'&gt;raphael.js&lt;/a&gt;. This is a good way to visualize browser marketshare data, page access trends, and other analytical data that they might want to drill down on.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>How to Accept Self-Signed SSL Certificates in Selenium</title>
    <link href="http://mogotest.com/blog/2010/04/13/how-to-accept-self-signed-ssl-certificates-in-selenium" />
    <updated>2010-04-13T00:00:00-04:00</updated>
    <id>http://mogotest.com/blog/2010/04/13/how-to-accept-self-signed-ssl-certificates-in-selenium</id>
    <content type="html">&lt;p&gt;Accessing pages with self-signed SSL certificates in Selenium can be a bit tricky. The core of the issue is every major browser raises a security issue when accessing a page using a self-signed SSL certificate. Since this security check takes effect before the Selenium Javascript can execute, there&amp;#8217;s no way to instruct the browser to accept the certificate.&lt;/p&gt;

&lt;h2 id='selenium_as_a_proxy_server'&gt;Selenium as a Proxy Server&lt;/h2&gt;

&lt;p&gt;As it turns out, there is a solution built into Selenium, but there&amp;#8217;s some work involved to get it going properly. Unless you use the &lt;code&gt;-avoidProxy&lt;/code&gt; argument when you start up the Selenium server, it will serve as a proxy for connections originating on localhost. If you start the Selenium server with the &lt;code&gt;-trustAllSSLCertificates&lt;/code&gt; argument the proxy will be able to handle any type of SSL certificate issue for any site. The corollary to using Selenium server as a proxy is that your browser sessions need to be configured to use the Selenium server as a proxy. It should be noted that this is not the same as proxy injection mode; Selenium is not injecting itself into your page, it&amp;#8217;s simply proxying the content for you.&lt;/p&gt;

&lt;h2 id='configuring_the_selenium_browser_launchers'&gt;Configuring the Selenium Browser Launchers&lt;/h2&gt;

&lt;p&gt;You configure the browser&amp;#8217;s proxy settings through the browser launcher. Some of the provided launchers do this already. For Internet Explorer you can use the &lt;code&gt;*iexploreproxy&lt;/code&gt; launcher and for Firefox you can use the &lt;code&gt;*firefoxproxy&lt;/code&gt; launcher. Another option is to use a custom browser launcher. We do this with both Firefox and Internet Explorer and the code can be found on our &lt;a href='http://github.com/mogo/selenium'&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We found the default chrome launcher for Firefox is very close to what we want and didn&amp;#8217;t want to have to manage our own custom profiles. So we use that launcher as a base and modified it such that if Selenium server is started with &lt;code&gt;-trustAllSSLCertificates&lt;/code&gt; option then then generated Firefox profile will set up the Selenium server as the proxy. The &lt;a href='http://gist.github.com/364250'&gt;patch&lt;/a&gt; actually modifies the &lt;code&gt;*firefox&lt;/code&gt; launcher, but a custom launcher is likely a safer option for you.&lt;/p&gt;

&lt;p&gt;The reason we use a custom launcher with Internet Explorer is that we&amp;#8217;ve experienced reliability issues with the &lt;code&gt;*iexploreproxy&lt;/code&gt; launcher. We&amp;#8217;ve found &lt;code&gt;*iexplore&lt;/code&gt; to be more reliable so our custom launcher simply configures Internet Explorer to use Selenium as its proxy server and then uses HTA to perform the browser automation functions, similar to how &lt;code&gt;*iexplore&lt;/code&gt; operates. The &lt;a href='http://github.com/mogo/selenium/blob/master/remote/server/src/java/org/openqa/selenium/server/browserlaunchers/InternetExplorerMogoLauncher.java'&gt;code for this launcher&lt;/a&gt; is also available in our GitHub repository.&lt;/p&gt;

&lt;p&gt;An extra step is required for proxying SSL content with Internet Explorer. Selenium bundles a special certification authority (CA) certificate that is used to trust all the other SSL certificates. Windows will not trust this CA certificate until it is installed in the trusted root store.&lt;/p&gt;

&lt;h2 id='installing_the_cybervillains_certificate_on_windows'&gt;Installing the CyberVillains Certificate on Windows&lt;/h2&gt;

&lt;p&gt;The following figures show the steps necessary to install the CyberVillains certificate on Windows. This is only necessary if you&amp;#8217;re using Internet Explorer. Selenium is able to manipulate Firefox to trust the certificate with its custom-built profile.&lt;/p&gt;

&lt;p&gt;The CyberVillains certificate is bundled in the most recent Selenium RC releases. If you download the distribution and extract it, you should be able to get going starting with Figure 1. In the explorer address bar in Figure 1 you can see where to find the certificate. You must substitute the base path (C:\, here) with wherever you extracted the files.&lt;/p&gt;
&lt;div class='figure'&gt;
  &lt;img src='/images/static/how-to-accept-self-signed-certificates-in-selenium/cybervillains_certificate.png' /&gt;

  Figure 1: Double-click the CyberVillains certificate in the selenium server distribution.
&lt;/div&gt;&lt;div class='figure'&gt;
  &lt;img src='/images/static/how-to-accept-self-signed-certificates-in-selenium/install_certificate.png' /&gt;

  Figure 2: Install the CyberVillains certificate.
&lt;/div&gt;&lt;div class='figure'&gt;
  &lt;img src='/images/static/how-to-accept-self-signed-certificates-in-selenium/certificate_import_wizard.png' /&gt;

  Figure 3: Click through the SSL certificate import wizard.
&lt;/div&gt;&lt;div class='figure'&gt;
  &lt;img src='/images/static/how-to-accept-self-signed-certificates-in-selenium/choosing_the_certificate_store.png' /&gt;

  Figure 4: Choose the Trusted Root Certification Authorities certificate store.
&lt;/div&gt;&lt;div class='figure'&gt;
  &lt;img src='/images/static/how-to-accept-self-signed-certificates-in-selenium/finish_wizard.png' /&gt;

  Figure 5: Complete the import.
&lt;/div&gt;&lt;div class='figure'&gt;
  &lt;img src='/images/static/how-to-accept-self-signed-certificates-in-selenium/security_warning.png' /&gt;

  Figure 6: Accept the security warning.
&lt;/div&gt;&lt;div class='figure'&gt;
  &lt;img src='/images/static/how-to-accept-self-signed-certificates-in-selenium/successful_import.png' /&gt;

  Figure 7: Wrap everything up.
&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;At this stage everything is set up and you should be able to run your Selenium server with the &lt;code&gt;-trustAllSSLCertificates&lt;/code&gt; argument without any problems.&lt;/p&gt;

&lt;h2 id='conclusion'&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;To recap, accepting self-signed certificates in Selenium is a two-staged process. You need to instruct the Selenium server to trust all SSL certificates and then tell the browser to use the Selenium server as a proxy. You may find that creating a custom browser launcher is the best way to configure your browser&amp;#8217;s proxy settings. After doing this, you&amp;#8217;ll be able to test sites running on domains with self-signed and invalid SSL certificates.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Selenium Grid News</title>
    <link href="http://mogotest.com/blog/2010/03/31/selenium-grid" />
    <updated>2010-03-31T00:00:00-04:00</updated>
    <id>http://mogotest.com/blog/2010/03/31/selenium-grid</id>
    <content type="html">&lt;p&gt;It&amp;#8217;s no secret that we&amp;#8217;re big fans of the &lt;a href='http://seleniumhq.org'&gt;Selenium&lt;/a&gt; project. Selenium&amp;#8217;s screen capture facilities provide a key bit of the infrastructure that Mogotest runs on. And, since we&amp;#8217;re distributing those jobs to our cloud-based render agents, we need a way to manage that, too. That&amp;#8217;s where Selenium Grid comes into play.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://selenium-grid.seleniumhq.org'&gt;Selenium Grid&lt;/a&gt; is basically a framework for distributing Selenium tasks across many hosts (in our case, via our EC2 render cluster). This allows us to run our tests in parallel and deliver results to users as quickly and efficiently as possible.&lt;/p&gt;

&lt;p&gt;Grid was originally developed and maintained by &lt;a href='http://ph7spot.com'&gt;Phillippe Hanrigou&lt;/a&gt; of ThoughtWorks and has received contributions from numerous people, including Mogo co-founder Kevin Menard. &lt;a href='http://ph7spot.com/blog/selenium-grid-needs-a-new-maintainer'&gt;Earlier this month&lt;/a&gt;, Phillippe announced that he would be stepping down from the project and was looking for a new maintainer. Kevin was one of the folks who was nominated to fill his shoes, and it now looks like &lt;a href='http://ph7spot.com/blog/new-selenium-grid-maintainer'&gt;he&amp;#8217;s officially got the gig&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What does this announcement mean for Selenium Grid? It means that the project will have an active maintainer who (despite his obvious personality flaws) has a serious commitment to pushing the project codebase forward, fixing bugs, and fostering community involvement &amp;#8211; things that every mature open source project needs. Good stuff! And it&amp;#8217;s good for us, too. For exactly the same reasons.&lt;/p&gt;

&lt;p&gt;So congratulations on the new responsibility, Kevin. I&amp;#8217;m definitely looking forward to seeing how Se Grid evolves under the new project leadership.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Web Development is Hard Work</title>
    <link href="http://mogotest.com/blog/2010/03/10/hello-mogo" />
    <updated>2010-03-10T00:00:00-05:00</updated>
    <id>http://mogotest.com/blog/2010/03/10/hello-mogo</id>
    <content type="html">&lt;p&gt;Web development can really suck. If you&amp;#8217;re building websites or applications that run on the Web, you&amp;#8217;re juggling a lot of different technologies: HTML, CSS, JavaScript, one or more of a dozen different server-side languages, database administration, blah blah blah.&lt;/p&gt;

&lt;p&gt;And, because you&amp;#8217;re a good developer &amp;#8211; the kind who wants their work to look and behave consistently for your users &amp;#8211; you want to test as much of this stuff as possible. Unit tests make a huge difference in the way we write and refactor code, but testing the actual end user experience&amp;#8230; that&amp;#8217;s harder to do.&lt;/p&gt;

&lt;p&gt;As web developers ourselves, we&amp;#8217;re acutely aware of some common pain points. One of the biggest ones is cross-browser testing. Doing it by hand is downright painful, and requires a veritable QA lab of different platforms and browsers. And different versions of browsers. And service patches. And updates. That can be a lot of work, especially if you&amp;#8217;re a small shop, overworked, or simply have better things to deal with.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;re building Mogotest to make testing your websites and Web-based applications easier. So you can spend less time checking your site in different browsers and more time concentrating on the features that make your project truly unique.&lt;/p&gt;

&lt;p&gt;What we do right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-browser render testing&lt;/li&gt;

&lt;li&gt;View comparison tools&lt;/li&gt;

&lt;li&gt;Validation and verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where we want to get to (the cool stuff):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full-on view-level regression testing&lt;/li&gt;

&lt;li&gt;Providing tools that make it easier for you to detect and fix common rendering problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The service is in private alpha right now. But if you&amp;#8217;re interested, please &lt;a href='http://mogotest.com'&gt;sign up&lt;/a&gt; and we&amp;#8217;ll get you an invitation as soon as more are available.&lt;/p&gt;

&lt;p&gt;Thanks for your interest in Mogo!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;*back to work*&lt;/em&gt;&lt;/p&gt;</content>
  </entry>
  
</feed>
