forked from thoughtbot/shoulda-matchers
-
Notifications
You must be signed in to change notification settings - Fork 0
/
route_matcher.rb
148 lines (137 loc) · 4.71 KB
/
route_matcher.rb
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
module Shoulda
module Matchers
module ActionController
# The `route` matcher tests that a route resolves to a controller,
# action, and params; and that the controller, action, and params
# generates the same route. For an RSpec suite, this is like using a
# combination of `route_to` and `be_routable`. In a test suite using
# Minitest + Shoulda, it provides a more expressive syntax over
# `assert_routing`.
#
# You can use this matcher either in a controller test case or in a
# routing test case. For instance, given these routes:
#
# My::Application.routes.draw do
# get '/posts', controller: 'posts', action: 'index'
# get '/posts/:id' => 'posts#show'
# end
#
# You could choose to write tests for these routes alongside other tests
# for PostsController:
#
# class PostsController < ApplicationController
# # ...
# end
#
# # RSpec
# RSpec.describe PostsController, type: :controller do
# it { should route(:get, '/posts').to(action: :index) }
# it { should route(:get, '/posts/1').to(action: :show, id: 1) }
# end
#
# # Minitest (Shoulda)
# class PostsControllerTest < ActionController::TestCase
# should route(:get, '/posts').to(action: 'index')
# should route(:get, '/posts/1').to(action: :show, id: 1)
# end
#
# Or you could place the tests along with other route tests:
#
# # RSpec
# describe 'Routing', type: :routing do
# it do
# should route(:get, '/posts').
# to(controller: :posts, action: :index)
# end
#
# it do
# should route(:get, '/posts/1').
# to('posts#show', id: 1)
# end
# end
#
# # Minitest (Shoulda)
# class RoutesTest < ActionController::IntegrationTest
# should route(:get, '/posts').
# to(controller: :posts, action: :index)
#
# should route(:get, '/posts/1').
# to('posts#show', id: 1)
# end
#
# Notice that in the former case, as we are inside of a test case for
# PostsController, we do not have to specify that the routes resolve to
# this controller. In the latter case we specify this using the
# `controller` key passed to the `to` qualifier.
#
# #### Qualifiers
#
# ##### to
#
# Use `to` to specify the action (along with the controller, if needed)
# that the route resolves to.
#
# # Three ways of saying the same thing (using the example above)
# route(:get, '/posts').to(action: index)
# route(:get, '/posts').to(controller: :posts, action: index)
# route(:get, '/posts').to('posts#index')
#
# If there are parameters in your route, then specify those too:
#
# route(:get, '/posts/1').to('posts#show', id: 1)
#
# You may also specify special parameters such as `:format`:
#
# route(:get, '/posts').to('posts#index', format: :json)
#
# @return [RouteMatcher]
#
def route(method, path)
RouteMatcher.new(method, path, self)
end
# @private
class RouteMatcher
def initialize(method, path, context)
@method = method
@path = path
@context = context
end
attr_reader :failure_message, :failure_message_when_negated
def to(*args)
@params = RouteParams.new(args).normalize
self
end
def in_context(context)
@context = context
self
end
def matches?(controller)
guess_controller!(controller)
route_recognized?
end
def description
"route #{@method.to_s.upcase} #{@path} to/from #{@params.inspect}"
end
private
def guess_controller!(controller)
@params[:controller] ||= controller.controller_path
end
def route_recognized?
begin
@context.__send__(:assert_routing,
{ method: @method, path: @path },
@params)
@failure_message_when_negated = "Didn't expect to #{description}"
true
rescue ::ActionController::RoutingError => error
@failure_message = error.message
false
rescue Shoulda::Matchers.assertion_exception_class => error
@failure_message = error.message
false
end
end
end
end
end
end