Testing Facebook Login and Permissions With Capybara

Having begun to use Capybara and Rspec quiet heavily there have been a few challenges that need to be overcome. One of these is that the applications I develop day to day are heavily intertwined with Facebook and user flow relies heavily on Facebook login and accepting permissions at various points throughout the application.

Outside of the Facebook chrome (the non iframed version of the application) its quite easy interact with these events as if we were a normal user.

Use Facebook Test Users

First go to your Facebook applications roles dashboard at https://developers.facebook.com/apps/[APPID]/roles and create some test users and if needed make them friends with one another. Take a note of there email, fbid and create a password for them.

Capybara Settings

Since Facebook is an external service we need to extend Capybaras default wait time incase its being slow and also change the host that the specs run under so that it matches the settings for our Facebook app.

1
2
Capybara.default\_wait\_time = 10
Capybara.app_host = 'http://' + Settings.host

Test User Factory

The FactoryGirl gem provides a great way to create and manage test data based on rails models. FactoryGirl can even be used with things other than models so to keep my actual user model separate from my test users data I created a special TestUser class. By placing it in a subfolder of the support folder the default installation will load all files and sub folders at initialisations allowing us access to this special class in our tests.

1
2
3
4
# spec/support/models/test_user.rb
class TestUser
   attr_accessor  :email, :password, :fbid, :name
end


With this class its then easily to make a test_user factory as you would a rails model factory. In this instance iv used a nested factory for additional test users with different states.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 FactoryGirl.define do
   factory :test_user do |f|
     name 'Bob Ameibdcaahgd Liangman'
     fbid '100005892102238'
     email 'oyncoeh_liangman_1368123301@tfbnw.net'
     password 'XXXX'

     factory :test_user_mark do
       name 'Mark Ameiehbaakf Wisemanstein'
       fbid '100005958231006'
       email 'dqfaoxm_wisemanstein_13684123@tfbnw.net'
       password 'XXXX'
     end

     factory :test_user_arabic do
       name 'Richard Amehgghdihei Changescu'
       fbid '100005877823859'
       email 'lifczou_changescu_1368123920@tfbnw.net'
       password 'XXXX'
     end
   end
 end

Using these tests users in a capybara spec only has one caveat. They must be created in memory using build since there isn’t a database table for them to be saved to

1
2
3
let(:test_user_mark) do
  FactoryGirl.build(:test_user_mark)
end

Automating Facebook

The following module is a collection of macros that take care of the Facebook login and permissions dialogues. It attempts to deal with the various possible flows that can happen when using login with Facebook depending on wether the user has logging in and is being asked for permissions in the same sequence or if the permission are taken at a different point. There is also some javascript macros for login the user out of Facebook and removing the app permissions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# spec/support/facebook_macros
module FacebookMacros

  def complete_facebook_dialogues_on_click(selector, test_user)
    # until bookface version bumped to latest we have no way to know its ready so delay, not ideal
    sleep 1
    find("#{selector}").click
    return if page.driver.browser.window_handles.length == 1
    within_window('Facebook') do
      fill_in_facebook_form(test_user) if page.has_css?('#loginbutton')
      accept_permissions_outside_facebook
    end
  end

  def fill_in_facebook_form(test_user)
    fill_in('email', :with => "#{test_user.email}")
    fill_in('pass', :with => "#{test_user.password}")
    find('#loginbutton').click
  end

  def accept_additional_permissions
    return if page.driver.browser.window_handles.length == 1
    within_window('Facebook') do
      accept_permissions_outside_facebook
    end
  end

  def accept_permissions_outside_facebook
    find(:xpath, "//button[@name='__CONFIRM__']").click if page.driver.browser.window_handles.length == 2
    find(:xpath, "//button[@name='__CONFIRM__']").click if page.driver.browser.window_handles.length == 2
  end

  def deauth_app
    sleep 1
    page.execute_script %Q{
       FB.api("/me/permissions","DELETE", function(response){
         console.log(response)
       });
     }
    sleep 1
  end

  def logout
    sleep 1
    page.execute_script %Q{
      FB.logout(function(response) {
        // user is now logged out
      });
     }
    sleep 1
  end

end

The above module can be easily included in Rspec’s configure block with the config.includes command

1
2
3
4
5
6
7
RSpec.configure do |config|

  ....
  config.include FacebookMacros
  ....

end

An example scenario using these macros. Its important that they are run with javascript enabled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
scenario 'user import friends', :js => true do
  visit root_path
  complete_facebook_dialogues_on_click('#auth_me', test_user)
  page.should have_css('#entry_step_1')
  visit home_dashboard_path


  expect{

    sleep 2
    find('#add_events').click

    accept_additional_permissions
    sleep 2

    page.should_not have_css('#add_events')
    page.should have_css('.events_item')
    # deal with additional perms
  }.to change(Event, :count)


  logout
  Capybara.reset_session!
end

Comments