Skip to content

Commit

Permalink
Add a new Routing module
Browse files Browse the repository at this point in the history
Why:

* The `route` matcher can not only be used within controller example
  groups but also routing example groups.
* Now that we are mixing matchers into specific example groups, `route`
  is no longer available in routing example groups.

To satisfy the above:

* Create a new module that contains a `route` method and returns a new
  instance of RouteMatcher. (RouteMatcher still lives in the
  ActionController namespace.)
* Mix this module into routing example groups when the gem configuration
  block is run.
  • Loading branch information
mcmire committed Sep 25, 2015
1 parent af98a23 commit 8cf449b
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/shoulda/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
require 'shoulda/matchers/action_controller'
require 'shoulda/matchers/active_model'
require 'shoulda/matchers/active_record'
require 'shoulda/matchers/routing'

module Shoulda
module Matchers
Expand Down
1 change: 1 addition & 0 deletions lib/shoulda/matchers/integrations/libraries.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'shoulda/matchers/integrations/libraries/active_record'
require 'shoulda/matchers/integrations/libraries/missing_library'
require 'shoulda/matchers/integrations/libraries/rails'
require 'shoulda/matchers/integrations/libraries/routing'

module Shoulda
module Matchers
Expand Down
3 changes: 2 additions & 1 deletion lib/shoulda/matchers/integrations/libraries/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class Rails
SUB_LIBRARIES = [
:active_model,
:active_record,
:action_controller
:action_controller,
:routing
]

def integrate_with(test_framework)
Expand Down
27 changes: 27 additions & 0 deletions lib/shoulda/matchers/integrations/libraries/routing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Shoulda
module Matchers
module Integrations
module Libraries
# @private
class Routing
Integrations.register_library(self, :routing)

include Integrations::Inclusion
include Integrations::Rails

def integrate_with(test_framework)
test_framework.include(matchers_module, type: :routing)

include_into(::ActionController::TestCase, matchers_module)
end

private

def matchers_module
Shoulda::Matchers::Routing
end
end
end
end
end
end
10 changes: 10 additions & 0 deletions lib/shoulda/matchers/routing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Shoulda
module Matchers
module Routing
# @private
def route(method, path)
ActionController::RouteMatcher.new(method, path, self)
end
end
end
end
242 changes: 242 additions & 0 deletions spec/unit/shoulda/matchers/routing/route_matcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
require 'unit_spec_helper'

describe 'Shoulda::Matchers::Routing::RouteMatcher', type: :routing do
before do
define_controller('ThingsController')
end

shared_examples_for 'core tests' do
context 'when the given method, path, controller, and action match an existing route' do
it 'accepts' do
define_routes { get '/', to: 'things#index' }

assert_accepts add_target_to(
route(:get, '/'),
controller: 'things',
action: 'index'
)
end

context 'and the expected controller is specified as a symbol' do
it 'accepts' do
define_routes { get '/', to: 'things#index' }

assert_accepts add_target_to(
route(:get, '/'),
controller: :things,
action: 'index'
)
end
end

context 'and the expected action is specified as a symbol' do
it 'accepts' do
define_routes { get '/', to: 'things#index' }

assert_accepts add_target_to(
route(:get, '/'),
controller: 'things',
action: :index
)
end
end
end

context 'when the given method, path, controller, and action do not match an existing route' do
it 'rejects' do
assert_rejects add_target_to(
route(:get, '/non_existent_route'),
controller: 'no_controller',
action: 'no_action'
)
end
end

context 'when the given path, controller, and action match an existing route but the method does not' do
it 'rejects' do
define_routes { post '/', to: 'things#index' }

assert_rejects add_target_to(
route(:get, '/'),
controller: 'things',
action: 'index'
)
end
end

context 'when the given method, controller, and action match an existing route but the path does not' do
it 'rejects' do
define_routes { get '/', to: 'things#index' }

assert_rejects add_target_to(
route(:get, '/different_path'),
controller: 'things',
action: 'index'
)
end
end

context 'when the given method and path match an existing route but the controller does not' do
it 'rejects' do
define_routes { get '/', to: 'another_controller#index' }

assert_rejects add_target_to(
route(:get, '/'),
controller: 'things',
action: 'index'
)
end
end

context 'when the given method, path, and controller match an existing route but the action does not' do
it 'rejects' do
define_routes { get '/', to: 'things#index' }

assert_rejects add_target_to(
route(:get, '/'),
controller: 'things',
action: 'another_action'
)
end
end

context 'when the actual route has a param' do
context 'and the expected params include that param' do
it 'accepts' do
define_routes { get '/things/:id', to: 'things#show' }

assert_accepts add_target_to(
route(:get, '/things/1'),
controller: 'things',
action: 'show',
id: '1'
)
end

context 'but its value was not specified as a string' do
it 'accepts, treating it as a string' do
define_routes { get '/things/:id', to: 'things#show' }

assert_accepts add_target_to(
route(:get, '/things/1'),
controller: 'things',
action: 'show',
id: 1
)
end
end
end

context 'and the expected params do not match the actual params' do
it 'rejects' do
define_routes { get '/things/:id', to: 'things#show' }

params = {
controller: 'things',
action: 'show',
some: 'other',
params: 'here'
}
assert_rejects add_target_to(
route(:get, '/things/:id'),
params
)
end
end
end

context 'when the actual route has a default param whose value is a symbol' do
context 'and the expected params include a value for it' do
context 'as a symbol' do
it 'accepts' do
define_routes do
post '/things(.:format)',
to: 'things#create',
defaults: { format: :json }
end

assert_accepts add_target_to(
route(:post, '/things'),
controller: 'things',
action: 'create',
format: :json
)
end
end

context 'as a string' do
it 'accepts' do
define_routes do
post '/things(.:format)',
to: 'things#create',
defaults: { format: :json }
end

assert_accepts add_target_to(
route(:post, '/things'),
controller: 'things',
action: 'create',
format: 'json'
)
end
end
end
end

context 'when the existing route has a glob segment' do
context 'and a param is given which represents the segment' do
it 'accepts' do
define_routes { get '/things/*id', to: 'things#whatever' }

assert_accepts add_target_to(
route(:get, '/things/foo/bar'),
controller: 'things',
action: 'whatever',
id: 'foo/bar'
)
end
end

context 'and no param is given which represents the segment' do
it 'rejects' do
define_routes { get '/things/*id', to: 'things#whatever' }

assert_rejects add_target_to(
route(:get, '/things'),
controller: 'things',
action: 'whatever'
)
end
end
end
end

context 'given a controller and action specified as individual options' do
include_examples 'core tests'

def add_target_to(route_matcher, params)
route_matcher.to(params)
end
end

context 'given a controller and action joined together in a string' do
include_examples 'core tests'

def add_target_to(route_matcher, args)
controller = args.fetch(:controller)
action = args.fetch(:action)
route_matcher.to(
"#{controller}##{action}",
args.except(:controller, :action)
)
end
end

def assert_accepts(matcher)
should(matcher)
end

def assert_rejects(matcher)
should_not(matcher)
end
end

0 comments on commit 8cf449b

Please sign in to comment.