Month: January 2013

my favorite cucumber tricks

1. uh oh, time to debug
with your ruby-debug gem, you can do the following:

Then /^I debug$/ do
  breakpoint
  0
end

or

When /^debugger$/ do
  debugger
end

2. I love firebug!

https://github.com/jfirebaugh/capybara-firebug

while in debugger mode, use firebug to select the element to make sure that we got the right one. Obviously, this only works if you run your cukes in firefox.

3. but Sherry, I wanna run just that one scenario…
add tag(s) or use the line number

#address_book.feature

@current
Scenario: Consumer sees the Address Book
    Given I am a user
    When I follow "Address Book"
    Then I should see "Alice"
    And I should see "WonderLand, CA, 12345"
    And I should see "Edit"
    And I should see "Delete"
    And I should see "Add an address"

cucumber –tags @current
or
cucumber features/address_book.feature:61

note: you can name your tag whatever you want: @current, @test, @bug, etc…

4. The page is taking a while to load?
and boom, the test fails because the element is not visible? Try this:

When /^(?:|I )wait until "([^"]*)" is visible$/ do |selector|
  keep_looping = 0
  max_loops = 6
  selector_visible = false
  while keep_looping < max_loops do
     if page.has_css?("#{selector}", :visible => true)
        selector_visible = true
        keep_looping = max_loops
     else
        keep_looping += 1
        puts keep_looping
     end
  end
  raise Capybara::TimeoutError, "Waiting for selector #{selector} to become visible took too long" unless selector_visible
end

note: for capybara 2.0, check out
why wait_until was removed from capybara
asynchronous javascript ajax and friends

5. I wanna see my scenario in slowww motion
cukes run fairly quickly, so use @pause (as a tag) to see it run step by step or use it to debug (with your firebug)!

AfterStep('@pause') do
  print "Press Return to continue"
  STDIN.getc
end

Language Selector

TASK:

  • Make a language selector for the user to choose which language he/she would like to see the page in.
  • note: The language selector only shows a list of languages that we support.

EX:

  • 1. In the language selector, show the default language (A) selected.
  • 2. When the user clicks on the current language (A), display a list of other supported languages (B,C,D,etc).
  • 3.  When the user selects a new language (B), hide the list of supported languages.
  • 4. When the user clicks on the current language (B), display a list of other supported languages (A,C,D,etc).

———————————————————————————————————————

let’s look at the view first for an overview:
# _language_selector.html.haml

#current_language{:onClick => "language_selector();"}
  .language_list.hidden
      %ul
        -other_languages(assumed_language).each do |x|
          %li
            =link_to t(x), :bank => params[:bank], :locale => language_to_locale(x), :country => Project::I18n.country_for_locale(language_to_locale(x))
   =t(assumed_language)
   =image_tag('blue_arrow_off.png', :class=> "language_arrow", :id=> "off_arrow")
   =image_tag('gold_arrow_on.png', :class=>"hidden language_arrow", :id=> "on_arrow")

# language_selector javascript:

function language_selector(){
    $('.language_list, #off_arrow, #on_arrow').toggle();
    $('#current_language').toggleClass("selected_language");
}

# sass related to the language_selector

.language_arrow{
  margin: 0 5px;
}

.language_selector{
  color: #007EAC;
  cursor: pointer;
  position: absolute;
  text-align: right;
}

#current_language {
  @extend .language_selector;
  li{
    position: relative;
  }
}

.language_list {
  @extend .language_selector;
  right: 5px;
  border: 1px solid #666;
  padding: 2px 16px 2px 35px;
  bottom: 15px;
  li{
    text-align: right;
  }
}

.selected_language{
  color: #FF0000;
}

.language_list (haml div) contains the list of languages that we support except the language selected already.

assumed_language is the language selected already, we say “assumed” here because we’re not sure if that is the preferred language yet when the user lands on the page for the first time.

selected_language is a color style (orange/gold) that is applied to the current language when the list of other languages is displayed, so that it matches with the on_arrow (orange/gold).

so… how did we get that language_list and assumed_language?

# language_helper.rb

include ActionView::Helpers::TranslationHelper

module LanguageHelper
  SUPPORTED_LANGUAGE = {
    # language => default locale
      "English" => :en,
      "Français" => :"fr-CA"
  }

  DEFAULT_LANGUAGE = "English"
  DEFAULT_LOCALE = I18n.default_locale

  #example: given locale :en or :en_CA, returns "en"
  def get_language(locale)
    locale.to_s[0..1] if locale
  end

  #returns the language given the locale, example :fr #=> "Français"
  def convert_locale_to_language(locale)
    given_locale = get_language(locale)
    # x returns an array of ["language", :locale]
    SUPPORTED_LANGUAGE.each do |x|

      #returns "en" from :en_CA
      shortened_locale = get_language(x[1])

      if shortened_locale == given_locale
        return x[0]
      end
    end

    #returns default language if all else fails
    DEFAULT_LANGUAGE
  end

  #example: given cookie[:locale] set up from app controller, returns "en"
  def assumed_language
    locale = cookies[:locale] ? get_language(cookies[:locale]) : DEFAULT_LOCALE
    convert_locale_to_language(locale)
  end

  def supported_languages
    SUPPORTED_LANGUAGE.keys
  end

  def other_languages(current_language = DEFAULT_LANGUAGE)
    supported_languages - current_language.to_a
  end

  def language_to_locale(language)
    SUPPORTED_LANGUAGE[language]
  end

end

remember to rspec

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
include ApplicationHelper

describe LanguageHelper do

  before :each do
    @locale = [:en, :en_CA, :"en-CA", "en", "en_CA", "en-CA"]
    @french_locale = [:fr, :"fr-CA", "fr"]
    @unsupported_locale = [:sp, :gr, "sp_MX", "sw_AF"]
  end

  describe "get_language" do
    it "should return the english locale as a string with only two characters" do
      @locale.each do |x|
        get_language(x).should eq("en")
      end
    end

    it "should return the french locale as a string with only two characters" do
      @french_locale.each do |x|
        get_language(x).should eq("fr")
      end
    end
  end

  describe "convert_locale_to_language" do
    it "should return the perspective language according to the locale if supported" do
      @locale.each do |x|
        convert_locale_to_language(x).should eq("English")
      end
    end

    it "should return French language given the French locale" do
      @french_locale.each do |x|
        convert_locale_to_language(x).should eq("Français")
      end
    end

    it "should return the default language if locale is not yet supported" do
      @unsupported_locale.each do |x|
        convert_locale_to_language(x).should eq("English")
      end
    end
  end

  describe "assumed_language" do
    context "obtain the assumed language from the locale cookie set up by application controller" do
      it "should return the language in English from the cookie with :en as its value" do
        helper.request.cookies[:locale] = :en
        assumed_language.should eq("English")
      end

      it "should return the language in French from the cookie with :fr as its value" do
        helper.request.cookies[:locale] = :fr
        assumed_language.should eq("Français")
      end
    end

    it "should set English as the default language if the cookie is missing" do
      assumed_language.should eq("English")
    end
  end

  describe "supported_languages" do
    it "should be an array" do
      supported_languages.should be_kind_of(Array)
    end
  end

  describe "other_language" do
    context "returns an array of supported languages except the language that is already selected" do
      it "should be an array" do
        other_languages.should be_kind_of(Array)
      end

      it "should not include the selected languages" do
        other_languages("English").should_not include("English")
        other_languages("Français").should_not include("Français")
      end
    end
  end

end

please enlighten me if you see code that I can improve on, thanks (:

GET a list of countries & more

GET

  • /countries

EX RESPONSE:

  • {“countryList”:[
  •       {“countryName”:”United States”,”countryCode”:”US”,”isoCode”:”840″},
  •       {“countryName”:”Canada”,”countryCode”:”CA”,”isoCode”:”124″}]
  • }

TASK:

  • retrieve the list of countries and format it, display countryName in specified language, etc.

——————————————————————————————————————-

module Project

  class ListCountries

    class << self       
      def current_list         
        begin           
          list_countries_url = APP_CONFIG['list_countries_url']           
          list_countries_headers = {               
              'Content-Type' => 'application/json',
              'Accept' => 'application/json',
              APP_CONFIG['api_key_header'] => APP_CONFIG['api_key']
          }
          response, body = APIClient.common_services_get(list_countries_url, list_countries_headers)
          ## APIClient makes HTTP calls with SSL set up (if uri.scheme == 'https') and headers and key and all that good stuff... ##
          if body
            JSON.parse(body)["countryList"]
          end
        rescue => e
          Rails.logger.debug "Project::ListCountries.current_list rescue => #{e.message}"
          raise
        end
      end
      #returns the list of countries
      # => [{"countryName"=>"United States", "countryCode"=>"US", "isoCode"=>"840"},
      #     {"countryName"=>"Canada", "countryCode"=>"CA", "isoCode"=>"124"}]

      def country_names
        current_list.collect{|x| x["countryName"]} unless current_list.nil?
      end
      #returns an array of country names
      # => ["United States", "Canada"]

      def alpha_country_codes
        current_list.collect{|x| x["countryCode"]} unless current_list.nil?
      end
      #returns an array of alpha country names
      # => ["US", "CA"]

      def iso_country_codes
        current_list.collect{|x| x["isoCode"]} unless current_list.nil?
      end
      #returns an array of iso country code
      # => ["840", "124"]

      def translated_country
        begin
          I18n.t(alpha_country_codes, :scope => "countries", :raise => true)
        rescue => e
          Rails.logger.debug "Project::ListCountries.translated_country rescue => #{e.message}"
          raise
        end
      end
      # returns translated countries
      # => ["United States", "Canada"] from en.yml
      # => ["États-Unis", "Canada"] from fr-CA.yml

      def translated_names_to_alpha
        unless current_list.nil?
          names_to_alpha = translated_country.each_with_index.collect{|country, index| {country => alpha_country_codes[index]}}
          names_to_alpha.inject{|k,v| k.merge v}
        end
      end
      # returns a Hash of translated country names to their alpha country codes
      # => {"United States" => "US", "Canada" => "CA"}
    end
  end
end

sample RSPEC:

require 'spec_helper'

describe "ListCountries" do

  context "successful response" do

    before(:each) do
      list_countries = [OpenStruct.new(:code => "200"), {"countryList" =>
                         [{"countryName" => "United States", "countryCode" => "US", "isoCode" => "840"},
                          {"countryName" => "Canada", "countryCode" => "CA", "isoCode" => "124"}]}.to_json]
      APIClient.stub(:common_services_get).and_return(list_countries)
    end


    describe "current_list" do
      it "should return an array of a list of countries" do
        result = Project::ListCountries.current_list
        result.should be_kind_of Array
      end
    end

    describe "country_names" do
      it "should return an array of country names" do
        result = Project::ListCountries.country_names
        result.should be_kind_of Array
        result.should include("United States")
      end
    end

    describe "translated_country" do
      it "should return an array of translated country names" do
        Project::ListCountries.stub(:alpha_country_codes).and_return(%w{US CA})
        Project::ListCountries.translated_country.should == %w{United\ States Canada}
      end

      it "should raise an exception when the country is not translated" do
        Project::ListCountries.stub(:alpha_country_codes).and_return(%w{SG LA})
        lambda { Project::ListCountries.translated_country }.should raise_error
      end
    end

    describe "translated_names_to_alpha_codes" do
      it "should return a hash of translated country names to their alpha country codes" do
        Project::ListCountries.stub(:alpha_country_codes).and_return(%w{US CA})
        Project::ListCountries.stub(:translated_country).and_return(%w{United\ States Canada})
        result = Project::ListCountries.translated_names_to_alpha
        result.should == {"United States" => "US", "Canada" => "CA"}
        result.should be_kind_of Hash
      end
    end
  end
end