js_rails_routes 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,199 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe JSRailsRoutes::Generator do
4
- let(:generator) do
5
- described_class.clone.instance
6
- end
4
+ include_context 'run in a sandbox'
5
+
6
+ subject(:generator) { described_class.new(builder, writable: writable) }
7
7
 
8
- it { expect(described_class).to include(Singleton) }
8
+ let(:writable) { spy('writable') }
9
+ let(:builder) { double('builder', build: result) }
10
+ let(:result) { Hash['Rails' => 'rails body', 'Admin::Engine' => 'admin body'] }
9
11
 
10
12
  describe '#generate' do
11
- let(:output_dir) { File.expand_path('spec/tmp') }
12
- subject do
13
- generator.output_dir = output_dir
14
- generator.generate(task)
15
- end
13
+ subject { generator.generate(task) }
16
14
 
17
- let(:task) do
18
- 'js:routes'
19
- end
15
+ let(:task) { 'js:routes' }
20
16
 
21
- it 'writes JS files' do
22
- expect(generator).to receive(:write)
23
- .with(be_in(['Rails', 'Admin::Engine']), a_string_including("rake #{task}"))
24
- .twice
17
+ it 'writes with path to file and its contents' do
18
+ allow(writable).to receive(:write)
25
19
  subject
26
- end
27
-
28
- context 'when actually creating files' do
29
- let(:js_files) { Dir.glob(File.join(output_dir, '{admin,rails}-routes.js')).map { |file| Pathname.new(file) } }
30
-
31
- after { FileUtils.rm_f(js_files) }
32
-
33
- it 'creates JS files' do
34
- subject
35
- expect(js_files).to all be_file
36
- end
37
- end
38
-
39
- context 'when include_paths is given' do
40
- before do
41
- generator.include_paths = include_paths
42
- end
43
-
44
- let(:include_paths) do
45
- %r{/new}
46
- end
47
-
48
- it 'writes paths matching with the parameter' do
49
- expect(generator).to receive(:write)
50
- .with(be_in(['Rails', 'Admin::Engine']), a_kind_of(String))
51
- .twice do |_, arg|
52
- paths = arg.split("\n")[(2 + described_class::PROCESS_FUNC.split("\n").size)..-1]
53
- expect(paths).not_to be_empty
54
- expect(paths).to all(match(include_paths))
55
- end
56
- subject
57
- end
58
- end
59
-
60
- context 'when exclude_paths is given' do
61
- before do
62
- generator.exclude_paths = exclude_paths
63
- end
64
-
65
- let(:exclude_paths) do
66
- %r{/new}
67
- end
68
-
69
- it 'writes paths not matching with the parameter' do
70
- expect(generator).to receive(:write)
71
- .with(be_in(['Rails', 'Admin::Engine']), a_kind_of(String))
72
- .twice do |_, arg|
73
- paths = arg.split("\n")[(2 + described_class::PROCESS_FUNC.split("\n").size)..-1]
74
- expect(paths).not_to be_empty
75
- paths.each do |path|
76
- expect(path).to_not match(exclude_paths)
77
- end
78
- end
79
- subject
80
- end
81
- end
82
-
83
- context 'when include_names is given' do
84
- before do
85
- generator.include_names = include_names
86
- end
87
-
88
- let(:include_names) do
89
- /user|note/
90
- end
91
-
92
- it 'writes paths matching with the parameter' do
93
- expect(generator).to receive(:write)
94
- .with(be_in(['Rails', 'Admin::Engine']), a_kind_of(String))
95
- .twice do |_, arg|
96
- paths = arg.split("\n")[(2 + described_class::PROCESS_FUNC.split("\n").size)..-1]
97
- expect(paths).not_to be_empty
98
- expect(paths).to all(match(include_names))
99
- end
100
- subject
101
- end
102
- end
103
-
104
- context 'when exclude_names is given' do
105
- before do
106
- generator.exclude_names = exclude_names
107
- end
108
-
109
- let(:exclude_names) do
110
- /user|note/
111
- end
112
-
113
- it 'writes paths not matching with the parameter' do
114
- expect(generator).to receive(:write)
115
- .with(be_in(['Rails', 'Admin::Engine']), a_kind_of(String))
116
- .twice do |_, arg|
117
- paths = arg.split("\n")[(2 + described_class::PROCESS_FUNC.split("\n").size)..-1]
118
- expect(paths).not_to be_empty
119
- paths.each do |path|
120
- expect(path).to_not match(exclude_names)
121
- end
122
- end
123
- subject
124
- end
125
- end
126
-
127
- context 'when exclude_engines is given' do
128
- before do
129
- generator.exclude_engines = exclude_engines
130
- end
131
-
132
- let(:exclude_engines) do
133
- /^admin/
134
- end
135
-
136
- let(:excluded_routes) do
137
- /note|photo/
138
- end
139
-
140
- it 'writes paths not matching with the parameter' do
141
- expect(generator).to receive(:write)
142
- .with(be_in(['Rails', 'Admin::Engine']), a_kind_of(String))
143
- .once do |_, arg|
144
- paths = arg.split("\n")[(2 + described_class::PROCESS_FUNC.split("\n").size)..-1]
145
- expect(paths).not_to be_empty
146
- paths.each do |path|
147
- expect(path).to_not match(excluded_routes)
148
- end
149
- end
150
- subject
151
- end
152
- end
153
-
154
- shared_examples_for 'writes paths that matches expected routes' do
155
- it do
156
- expect(generator).to receive(:write)
157
- .with(be_in(['Rails', 'Admin::Engine']), a_kind_of(String))
158
- .twice do |_, arg|
159
- paths = arg.split("\n")[(2 + described_class::PROCESS_FUNC.split("\n").size)..-1]
160
- expect(paths).not_to be_empty
161
- expect(paths).to include(match(expected_routes))
162
- end
163
- subject
164
- end
165
- end
166
-
167
- context 'when camelize is nil' do
168
- let(:expected_routes) do
169
- /blog_path|note_path/
170
- end
171
-
172
- it_behaves_like 'writes paths that matches expected routes'
173
- end
174
-
175
- context 'when camelize is :lower' do
176
- before do
177
- generator.camelize = :lower
178
- end
179
-
180
- let(:expected_routes) do
181
- /blogPath|notePath/
182
- end
183
-
184
- it_behaves_like 'writes paths that matches expected routes'
185
- end
186
-
187
- context 'when camelize is :upper' do
188
- before do
189
- generator.camelize = :upper
190
- end
191
-
192
- let(:expected_routes) do
193
- /BlogPath|NotePath/
194
- end
195
-
196
- it_behaves_like 'writes paths that matches expected routes'
20
+ expect(writable).to have_received(:write).with(
21
+ a_string_ending_with('app/assets/javascripts/rails-routes.js'),
22
+ a_string_including('rails body')
23
+ ).ordered
24
+ expect(writable).to have_received(:write).with(
25
+ a_string_ending_with('app/assets/javascripts/admin-routes.js'),
26
+ a_string_including('admin body')
27
+ ).ordered
197
28
  end
198
29
  end
199
30
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe JSRailsRoutes::Language::Base do
4
+ subject(:language) { described_class.new }
5
+
6
+ describe '#handle_route_set' do
7
+ subject { language.handle_route_set(double('route set')) }
8
+
9
+ it { expect { subject }.to raise_error(NotImplementedError) }
10
+ end
11
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe JSRailsRoutes::Language::JavaScript do
4
+ subject(:language) { described_class.new }
5
+
6
+ include_context 'run in a sandbox'
7
+
8
+ describe '::PROCESS_FUNC' do
9
+ subject { described_class::PROCESS_FUNC }
10
+
11
+ it 'returns a javascript function' do
12
+ is_expected.to eq <<~JAVASCRIPT
13
+ function process(route, params, keys) {
14
+ var query = [];
15
+ for (var param in params) if (params.hasOwnProperty(param)) {
16
+ if (keys.indexOf(param) === -1) {
17
+ query.push(param + "=" + encodeURIComponent(params[param]));
18
+ }
19
+ }
20
+ return query.length ? route + "?" + query.join("&") : route;
21
+ }
22
+ JAVASCRIPT
23
+ end
24
+ end
25
+
26
+ describe '#handle_route_set' do
27
+ subject { language.handle_route_set(route_set) }
28
+
29
+ let(:route_set) do
30
+ rails_route_set = ActionDispatch::Routing::RouteSet.new.tap do |routes|
31
+ routes.draw do
32
+ resources :articles
33
+ end
34
+ end
35
+ JSRailsRoutes::RouteSet.new('Rails', rails_route_set)
36
+ end
37
+
38
+ context 'without camelize option' do
39
+ it 'returns a javascript with snake_case functions' do
40
+ is_expected.to eq <<~JAVASCRIPT
41
+ #{described_class::PROCESS_FUNC}
42
+ export function articles_path(params) { return process('/articles', params, []); }
43
+ export function new_article_path(params) { return process('/articles/new', params, []); }
44
+ export function edit_article_path(params) { return process('/articles/' + params.id + '/edit', params, ['id']); }
45
+ export function article_path(params) { return process('/articles/' + params.id + '', params, ['id']); }
46
+ JAVASCRIPT
47
+ end
48
+ end
49
+
50
+ context 'with camelize = :lower option' do
51
+ before do
52
+ JSRailsRoutes.configure do |c|
53
+ c.camelize = :lower
54
+ end
55
+ end
56
+
57
+ it 'returns a javascript with lowerCamelCase functions' do
58
+ is_expected.to eq <<~JAVASCRIPT
59
+ #{described_class::PROCESS_FUNC}
60
+ export function articlesPath(params) { return process('/articles', params, []); }
61
+ export function newArticlePath(params) { return process('/articles/new', params, []); }
62
+ export function editArticlePath(params) { return process('/articles/' + params.id + '/edit', params, ['id']); }
63
+ export function articlePath(params) { return process('/articles/' + params.id + '', params, ['id']); }
64
+ JAVASCRIPT
65
+ end
66
+ end
67
+
68
+ context 'with camelize = :upper option' do
69
+ before do
70
+ JSRailsRoutes.configure do |c|
71
+ c.camelize = :upper
72
+ end
73
+ end
74
+
75
+ it 'returns a javascript with UpperCamelCase functions' do
76
+ is_expected.to eq <<~JAVASCRIPT
77
+ #{described_class::PROCESS_FUNC}
78
+ export function ArticlesPath(params) { return process('/articles', params, []); }
79
+ export function NewArticlePath(params) { return process('/articles/new', params, []); }
80
+ export function EditArticlePath(params) { return process('/articles/' + params.id + '/edit', params, ['id']); }
81
+ export function ArticlePath(params) { return process('/articles/' + params.id + '', params, ['id']); }
82
+ JAVASCRIPT
83
+ end
84
+ end
85
+
86
+ context 'with include_paths option' do
87
+ before do
88
+ JSRailsRoutes.configure do |c|
89
+ c.include_paths = /new/
90
+ end
91
+ end
92
+
93
+ it 'returns a javascript matching to the regexp' do
94
+ is_expected.to eq <<~JAVASCRIPT
95
+ #{described_class::PROCESS_FUNC}
96
+ export function new_article_path(params) { return process('/articles/new', params, []); }
97
+ JAVASCRIPT
98
+ end
99
+ end
100
+
101
+ context 'with exclude_paths option' do
102
+ before do
103
+ JSRailsRoutes.configure do |c|
104
+ c.exclude_paths = /new/
105
+ end
106
+ end
107
+
108
+ it 'returns a javascript not matching to the regexp' do
109
+ is_expected.to eq <<~JAVASCRIPT
110
+ #{described_class::PROCESS_FUNC}
111
+ export function articles_path(params) { return process('/articles', params, []); }
112
+ export function edit_article_path(params) { return process('/articles/' + params.id + '/edit', params, ['id']); }
113
+ export function article_path(params) { return process('/articles/' + params.id + '', params, ['id']); }
114
+ JAVASCRIPT
115
+ end
116
+ end
117
+
118
+ context 'with include_names option' do
119
+ before do
120
+ JSRailsRoutes.configure do |c|
121
+ c.include_names = /new/
122
+ end
123
+ end
124
+
125
+ it 'returns a javascript matching to the regexp' do
126
+ is_expected.to eq <<~JAVASCRIPT
127
+ #{described_class::PROCESS_FUNC}
128
+ export function new_article_path(params) { return process('/articles/new', params, []); }
129
+ JAVASCRIPT
130
+ end
131
+ end
132
+
133
+ context 'with exclude_names option' do
134
+ before do
135
+ JSRailsRoutes.configure do |c|
136
+ c.exclude_names = /new/
137
+ end
138
+ end
139
+
140
+ it 'returns a javascript not matching to the regexp' do
141
+ is_expected.to eq <<~JAVASCRIPT
142
+ #{described_class::PROCESS_FUNC}
143
+ export function articles_path(params) { return process('/articles', params, []); }
144
+ export function edit_article_path(params) { return process('/articles/' + params.id + '/edit', params, ['id']); }
145
+ export function article_path(params) { return process('/articles/' + params.id + '', params, ['id']); }
146
+ JAVASCRIPT
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe JSRailsRoutes::RouteSet do
4
+ subject(:route_set) { described_class.new(name, routes) }
5
+
6
+ include_context 'run in a sandbox'
7
+
8
+ let(:name) { 'Foo::Engine' }
9
+ let(:routes) do
10
+ ActionDispatch::Routing::RouteSet.new.tap do |routes|
11
+ routes.draw do
12
+ get '/articles' => 'articles#index'
13
+ get '/users' => 'users#index'
14
+ end
15
+ end
16
+ end
17
+
18
+ describe '.correct_matching_route_set_list' do
19
+ subject { described_class.correct_matching_route_set_list }
20
+
21
+ it 'returns an array of matching route sets' do
22
+ # See spec/support/test_app.rb
23
+ is_expected.to match [
24
+ be_a(described_class).and(have_attributes(name: 'Rails')).and(be_match),
25
+ be_a(described_class).and(have_attributes(name: 'Admin::Engine')).and(be_match)
26
+ ]
27
+ end
28
+ end
29
+
30
+ describe '#name' do
31
+ subject { route_set.name }
32
+
33
+ it { is_expected.to eq name }
34
+ end
35
+
36
+ describe '#routes' do
37
+ subject { route_set.routes }
38
+
39
+ it { is_expected.to all be_a(JSRailsRoutes::Route).and(be_match) }
40
+
41
+ context 'when some routes are excluded' do
42
+ before do
43
+ JSRailsRoutes.configure do |c|
44
+ c.exclude_names = /users/
45
+ end
46
+ end
47
+
48
+ it "doesn't include the excluded route" do
49
+ is_expected.to include be_a(JSRailsRoutes::Route).and(have_attributes(name: /articles/))
50
+ is_expected.not_to include be_a(JSRailsRoutes::Route).and(have_attributes(name: /users/))
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#match?' do
56
+ subject { route_set.match? }
57
+
58
+ it { is_expected.to be true }
59
+
60
+ context 'when exclude_engines option is specified' do
61
+ before do
62
+ JSRailsRoutes.configure do |c|
63
+ c.exclude_engines = exclude_engines
64
+ end
65
+ end
66
+
67
+ context 'and it matches to the name' do
68
+ let(:exclude_engines) { /Foo/ }
69
+
70
+ it { is_expected.to be false }
71
+ end
72
+
73
+ context 'and it does not match to the name' do
74
+ let(:exclude_engines) { /Bar/ }
75
+
76
+ it { is_expected.to be true }
77
+ end
78
+ end
79
+
80
+ context 'when routes are empty' do
81
+ before { allow(route_set).to receive(:routes).and_return([]) }
82
+
83
+ it { is_expected.to be false }
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe JSRailsRoutes::Route do
4
+ subject(:route) { described_class.new(raw_route) }
5
+
6
+ include_context 'run in a sandbox'
7
+
8
+ let(:raw_route) do
9
+ ActionDispatch::Routing::RouteSet.new.tap do |routes|
10
+ routes.draw do
11
+ get '/articles' => 'articles#index'
12
+ end
13
+ end.routes.first
14
+ end
15
+
16
+ describe '#name' do
17
+ subject { route.name }
18
+
19
+ it { is_expected.to eq 'articles' }
20
+ end
21
+
22
+ describe '#path' do
23
+ subject { route.path }
24
+
25
+ it { is_expected.to eq '/articles' }
26
+ end
27
+
28
+ describe '#match?' do
29
+ subject { route.match? }
30
+
31
+ it { is_expected.to be true }
32
+
33
+ context 'when include_paths option is specified' do
34
+ before do
35
+ JSRailsRoutes.configure do |c|
36
+ c.include_paths = include_paths
37
+ end
38
+ end
39
+
40
+ context 'and it matches to the path' do
41
+ let(:include_paths) { /articles/ }
42
+
43
+ it { is_expected.to be true }
44
+ end
45
+
46
+ context 'and it does not matche to the path' do
47
+ let(:include_paths) { /users/ }
48
+
49
+ it { is_expected.to be false }
50
+ end
51
+ end
52
+
53
+ context 'when exclude_paths option is specified' do
54
+ before do
55
+ JSRailsRoutes.configure do |c|
56
+ c.exclude_paths = exclude_paths
57
+ end
58
+ end
59
+
60
+ context 'and it matches to the path' do
61
+ let(:exclude_paths) { /articles/ }
62
+
63
+ it { is_expected.to be false }
64
+ end
65
+
66
+ context 'and it does not matche to the path' do
67
+ let(:exclude_paths) { /users/ }
68
+
69
+ it { is_expected.to be true }
70
+ end
71
+ end
72
+
73
+ context 'when include_names option is specified' do
74
+ before do
75
+ JSRailsRoutes.configure do |c|
76
+ c.include_names = include_names
77
+ end
78
+ end
79
+
80
+ context 'and it matches to the name' do
81
+ let(:include_names) { /articles/ }
82
+
83
+ it { is_expected.to be true }
84
+ end
85
+
86
+ context 'and it does not matche to the name' do
87
+ let(:include_names) { /users/ }
88
+
89
+ it { is_expected.to be false }
90
+ end
91
+ end
92
+
93
+ context 'when exclude_names option is specified' do
94
+ before do
95
+ JSRailsRoutes.configure do |c|
96
+ c.exclude_names = exclude_names
97
+ end
98
+ end
99
+
100
+ context 'and it matches to the name' do
101
+ let(:exclude_names) { /articles/ }
102
+
103
+ it { is_expected.to be false }
104
+ end
105
+
106
+ context 'and it does not matche to the name' do
107
+ let(:exclude_names) { /users/ }
108
+
109
+ it { is_expected.to be true }
110
+ end
111
+ end
112
+ end
113
+ end
@@ -2,8 +2,67 @@
2
2
 
3
3
  RSpec.describe JSRailsRoutes do
4
4
  describe '.configure' do
5
- it 'yields with Generator instance' do
6
- expect { |b| described_class.configure(&b) }.to yield_with_args(JSRailsRoutes::Generator.instance)
5
+ it 'yields with .config' do
6
+ expect { |b| described_class.configure(&b) }.to yield_with_args(described_class.config)
7
+ end
8
+ end
9
+
10
+ describe '.config' do
11
+ subject { described_class.config }
12
+
13
+ it { is_expected.to be_a JSRailsRoutes::Configuration }
14
+ end
15
+
16
+ describe '.sandbox' do
17
+ it 'yields within a new sandbox' do
18
+ original = described_class.config
19
+ described_class.sandbox do
20
+ expect(described_class.config).not_to be original
21
+ expect(described_class.config).to be_a JSRailsRoutes::Configuration
22
+ end
23
+ expect(described_class.config).to be original
24
+ end
25
+ end
26
+
27
+ describe '.generate_javascript' do
28
+ subject { described_class.generate_javascript(task) }
29
+
30
+ let(:task) { 'js:routes' }
31
+ let(:app_root) { JSRailsRoutes::SpecHelper::TestApp.root }
32
+
33
+ before do
34
+ FileUtils.rm_rf(app_root)
35
+ FileUtils.mkdir_p(app_root.join('app/assets/javascripts'))
36
+ end
37
+
38
+ it 'generates javascript files' do
39
+ subject
40
+
41
+ expect(File.read(app_root.join('app/assets/javascripts/rails-routes.js'))).to eq <<~JAVASCRIPT
42
+ // Don't edit manually. `rake #{task}` generates this file.
43
+ #{JSRailsRoutes::Language::JavaScript::PROCESS_FUNC}
44
+ export function blogs_path(params) { return process('/blogs', params, []); }
45
+ export function new_blog_path(params) { return process('/blogs/new', params, []); }
46
+ export function edit_blog_path(params) { return process('/blogs/' + params.id + '/edit', params, ['id']); }
47
+ export function blog_path(params) { return process('/blogs/' + params.id + '', params, ['id']); }
48
+ export function users_path(params) { return process('/users', params, []); }
49
+ export function new_user_path(params) { return process('/users/new', params, []); }
50
+ export function edit_user_path(params) { return process('/users/' + params.id + '/edit', params, ['id']); }
51
+ export function user_path(params) { return process('/users/' + params.id + '', params, ['id']); }
52
+ JAVASCRIPT
53
+
54
+ expect(File.read(app_root.join('app/assets/javascripts/admin-routes.js'))).to eq <<~JAVASCRIPT
55
+ // Don't edit manually. `rake #{task}` generates this file.
56
+ #{JSRailsRoutes::Language::JavaScript::PROCESS_FUNC}
57
+ export function notes_path(params) { return process('/notes', params, []); }
58
+ export function new_note_path(params) { return process('/notes/new', params, []); }
59
+ export function edit_note_path(params) { return process('/notes/' + params.id + '/edit', params, ['id']); }
60
+ export function note_path(params) { return process('/notes/' + params.id + '', params, ['id']); }
61
+ export function photos_path(params) { return process('/photos', params, []); }
62
+ export function new_photo_path(params) { return process('/photos/new', params, []); }
63
+ export function edit_photo_path(params) { return process('/photos/' + params.id + '/edit', params, ['id']); }
64
+ export function photo_path(params) { return process('/photos/' + params.id + '', params, ['id']); }
65
+ JAVASCRIPT
7
66
  end
8
67
  end
9
68
  end
data/spec/spec_helper.rb CHANGED
@@ -8,23 +8,7 @@ SimpleCov.start
8
8
  require 'rails/all'
9
9
  require 'js_rails_routes'
10
10
 
11
- class TestApp < Rails::Application
12
- config.root = File.expand_path('test_app', __dir__)
13
-
14
- routes.draw do
15
- resources :blogs
16
- resources :users
17
- end
18
- end
19
-
20
- module Admin
21
- class Engine < ::Rails::Engine
22
- routes.draw do
23
- resources :notes
24
- resources :photos
25
- end
26
- end
27
- end
11
+ Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f }
28
12
 
29
13
  RSpec.configure do |config|
30
14
  config.expect_with :rspec do |expectations|
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define_negated_matcher(:not_change, :change)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context 'run in a sandbox' do # rubocop:disable RSpec/ContextWording
4
+ around do |example|
5
+ JSRailsRoutes.sandbox { example.run }
6
+ end
7
+ end