Skip to content

Commit

Permalink
Added a Cucumber world that simulates canvas requests coming from fac…
Browse files Browse the repository at this point in the history
…ebook.
  • Loading branch information
bkeepers committed Mar 1, 2009
1 parent d8930f8 commit cad4ef4
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 0 deletions.
50 changes: 50 additions & 0 deletions lib/facebooker/mock/service.rb
@@ -0,0 +1,50 @@
require 'digest/md5'
require 'facebooker/service'

module Facebooker
# A mock service that reads the Facebook response from fixtures
# Adapted from http://gist.github.com/44344
#
# Facebooker::MockService.fixture_path = 'path/to/dir'
# Facebooker::Session.current = Facebooker::MockSession.create
#
class MockService < Service
class << self
attr_accessor :fixture_path
end

def read_fixture(method, filename, original = nil)
path = fixture_path(method, filename)
File.read path
rescue Errno::ENAMETOOLONG
read_fixture(method, hash_fixture_name(filename), filename)
rescue Errno::ENOENT => e
if File.exists?(fixture_path(method, 'default'))
File.read fixture_path(method, 'default')
else
e.message << "\n(Non-hashed path is #{original})" if original
e.message << "\nFacebook API Reference: http://wiki.developers.facebook.com/index.php/#{method.sub(/^facebook\./, '')}#Example_Return_XML"
raise e
end
end

def post(params)
method = params.delete(:method)
params.delete_if {|k,_| [:v, :api_key, :call_id, :sig].include?(k) }
Parser.parse(method, read_fixture(method, fixture_name(params)))
end

private
def fixture_path(method, filename)
File.join(self.class.fixture_path, method, "#{filename}.xml")
end

def hash_fixture_name(filename)
Digest::MD5.hexdigest(filename)
end

def fixture_name(params)
params.map {|*args| args.join('=') }.sort.join('&')
end
end
end
18 changes: 18 additions & 0 deletions lib/facebooker/mock/session.rb
@@ -0,0 +1,18 @@
require 'facebooker/session'

module Facebooker
class MockSession < Session
def secured?
true
end

def secure!
@uid = 1
true
end

def service
@service ||= MockService.new(Facebooker.api_server_base, Facebooker.api_rest_path, @api_key)
end
end
end
30 changes: 30 additions & 0 deletions lib/facebooker/rails/cucumber.rb
@@ -0,0 +1,30 @@
require 'facebooker/rails/cucumber/world'
require 'facebooker/mock/session'
require 'facebooker/mock/service'

Facebooker::MockService.fixture_path = File.join(RAILS_ROOT, 'features', 'support', 'facebook')

Facebooker::Session.current = Facebooker::MockSession.create
class ApplicationController
def facebook_session
Facebooker::Session.current
end
end

module Facebooker
class << self
# prevent Facebooker from adding canvas name as prefix to URLs
def request_for_canvas(arg)
yield
end
end

module Rails
module Controller
# prevent Facebooker from rendering fb:redirect
def redirect_to(*args)
super
end
end
end
end
46 changes: 46 additions & 0 deletions lib/facebooker/rails/cucumber/world.rb
@@ -0,0 +1,46 @@
require 'cucumber/rails/world'
require 'facebooker/rails/integration_session'

module Facebooker
module Rails
module Cucumber
class World < ::Cucumber::Rails::World
def open_session
session = Facebooker::Rails::IntegrationSession.new

# delegate the fixture accessors back to the test instance
extras = Module.new { attr_accessor :delegate, :test_result }
if self.class.respond_to?(:fixture_table_names)
self.class.fixture_table_names.each do |table_name|
name = table_name.tr(".", "_")
next unless respond_to?(name)
extras.__send__(:define_method, name) { |*args| delegate.send(name, *args) }
end
end

# delegate add_assertion to the test case
extras.__send__(:define_method, :add_assertion) { test_result.add_assertion }
session.extend(extras)
session.delegate = self
session.test_result = @_result

yield session if block_given?
session
end

def without_canvas
in_canvas = @integration_session.canvas
@integration_session.canvas = false
yield
@integration_session.canvas = in_canvas
end
end
end
end
end

World do
w = Facebooker::Rails::Cucumber::World.new
w.reset!
w
end
40 changes: 40 additions & 0 deletions lib/facebooker/rails/integration_session.rb
@@ -0,0 +1,40 @@
require 'action_controller/integration'

class Facebooker::Rails::IntegrationSession < ActionController::Integration::Session
include Facebooker::Rails::TestHelpers
attr_accessor :default_request_params, :canvas

def process(method, path, parameters = nil, headers = nil)
if canvas
parameters = facebook_params(@default_request_params.merge(parameters || {}))
end
super method, path, parameters, headers
end

def reset!
self.default_request_params = {:fb_sig_in_canvas => '1'}.with_indifferent_access
self.canvas = true
super
end

def get_with_canvas(path, parameters = nil, headers = nil)
if canvas
post path, (parameters || {}).merge('fb_sig_request_method' => 'GET'), headers
else
get_without_canvas path, parameters, headers
end
end
alias_method_chain :get, :canvas

%w(put delete).each do |method|
define_method "#{method}_with_canvas" do |*args|
if canvas
path, parameters, headers = *args
post path, (parameters || {}).merge('_method' => method.upcase), headers
else
send "#{method}_without_canvas", *args
end
end
alias_method_chain method, :canvas
end
end

4 comments on commit cad4ef4

@millisami
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As on line 26,
e.message << "\nFacebook API Reference: http://wiki.developers.facebook.com/index.php/#{method.sub(/^facebook\./, '')}#Example_Return_XML"

I couldn't get the Returning XML.
Where can I get the Example Returning XML files on the wiki?? I searched but couldn't find out!

@dpickett
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does sending facebook session variables depend on whether this is in canvas or not? I'd love to use this for testing a facebook connect application, but I can't because it wants to redirect to canvas. If a set in_canvas to false, it won't merge in the facebook args I need.

@mmangino
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because facebook connect doesn't send the variables. It uses cookies. If you want to test facebook connect, stub the session.

@dpickett
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I think understand the intent better now.

I can't stub the session in cucumber, though. I'd be happy to write something like this that injects the stuff we need into the cuke session if that's what's desired, but I didn't want to deviate off this class if that's what I should have been using. Perhaps I should write a ConnectIntegrationSession?

Please sign in to comment.