roundabout 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/README.md +19 -25
  4. data/Rakefile +8 -0
  5. data/app/assets/javascripts/roundabout/application.js +0 -1
  6. data/app/controllers/roundabout/application_controller.rb +3 -1
  7. data/app/controllers/roundabout/roundabout_controller.rb +5 -3
  8. data/app/views/roundabout/roundabout/index.html.erb +16 -2
  9. data/config/routes.rb +3 -1
  10. data/example_diagram.png +0 -0
  11. data/lib/roundabout.rb +7 -1
  12. data/lib/roundabout/engine.rb +2 -0
  13. data/lib/roundabout/minitest.rb +5 -0
  14. data/lib/roundabout/monkey/action_controller.rb +17 -10
  15. data/lib/roundabout/monkey/capybara.rb +45 -21
  16. data/lib/roundabout/railtie.rb +28 -7
  17. data/lib/roundabout/recorder.rb +2 -0
  18. data/lib/roundabout/rspec.rb +3 -2
  19. data/lib/roundabout/test-unit.rb +5 -0
  20. data/lib/roundabout/version.rb +3 -1
  21. data/roundabout.gemspec +11 -1
  22. data/test/app/assets/javascripts/application.js +14 -0
  23. data/test/app/assets/stylesheets/application.css +15 -0
  24. data/test/app/assets/stylesheets/scaffolds.scss +84 -0
  25. data/test/app/views/layouts/application.html.erb +15 -0
  26. data/test/app/views/users/_form.html.erb +22 -0
  27. data/test/app/views/users/edit.html.erb +6 -0
  28. data/test/app/views/users/index.html.erb +27 -0
  29. data/test/app/views/users/new.html.erb +5 -0
  30. data/test/app/views/users/show.html.erb +9 -0
  31. data/test/application_system_test_case.rb +7 -0
  32. data/test/dummy_app.rb +104 -0
  33. data/test/public/favicon.ico +0 -0
  34. data/test/system/roundabout_test.rb +71 -0
  35. data/test/test_helper.rb +25 -0
  36. data/vendor/assets/javascripts/raphael-min.js +1 -13
  37. metadata +177 -21
  38. data/app/assets/javascripts/roundabout/roundabout.js.coffee +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dcb58b4a2e5cc4434b6cfe400420f51ca98db935
4
- data.tar.gz: 1075039a0f8931e5f680372c88a77f86bee32a98
2
+ SHA256:
3
+ metadata.gz: 3e04a70b5737616244de5b9c4fa9d71fb3befb049732f9690dbc0e7b8a41e0b9
4
+ data.tar.gz: 79bf12bc73b6aff9a9caad512bb9d7a5a668847a56914cbe40b35da6a5e107ac
5
5
  SHA512:
6
- metadata.gz: 1cb6ac0560493f4ceb19d0f6291d367d31d1d68976cfc760bcfa4ffbca7058f426b6e543848fd12ec591061a7714e0137fb315778c1c3fc6d7385ccce5615ee2
7
- data.tar.gz: 691efe7c90e8e138e04179a5a662a533e53118a4378b85b694f601f109a426693a8007ab56b551a9f625e35e3b3b6a65a3c68680026ef58e4b60e7a4408d5d9a
6
+ metadata.gz: a9e1cc7575ed994138a771b4807c95586b7d776026a46acefc91e46550dcbe5f76e111e4fc80967769cb0022bcbdf3c107a7ec6db31aeb949451e45ea2806fb6
7
+ data.tar.gz: b2a3f5e70f1081e13c45a53aa9301a92ca15901a7d3fd11682fec3908c62315ab1b93dbf558218ed13a696b34e88d9ca069c3c32e695d05c33b8fdd77f66095a
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ test/log
data/README.md CHANGED
@@ -1,58 +1,52 @@
1
1
  # Roundabout
2
2
 
3
- A Rails Engine that generates a page transition diagram for your Rails app.
3
+ A Rails Engine that generates and shows a page transition diagram for your Rails app from system tests.
4
+
5
+ ![Example](example_diagram.png)
4
6
 
5
7
 
6
8
  ## Requirements
7
9
 
8
- - Ruby 1.9 or 2.0
10
+ - Ruby 2.x
9
11
 
10
- - Rails 3 or 4
12
+ - Rails 3 or newer
11
13
 
12
- - RSpec 2
14
+ - RSpec / Minitest / TestUnit
13
15
 
14
- - Capybara 1 or 2
16
+ - Capybara
15
17
 
16
18
 
17
19
  ## Installation
18
20
 
19
- Add this line to your Rails app's Gemfile:
21
+ Bundle this gem to your Rails app's development and test env:
20
22
 
21
23
  ```ruby
22
- gem 'roundabout'
24
+ gem 'roundabout', group :development, :test
23
25
  ```
24
26
 
25
- And execute:
26
27
 
27
- ```bash
28
- % bundle
29
- ```
28
+ ## Usage
30
29
 
31
- Then add this line to your `spec/spec_helper.rb`:
30
+ Run the whole tests with `ROUNDABOUT` envvar (I suppose parallel spec is not supported ATM):
32
31
 
33
- ```ruby
34
- require 'roundabout/rspec'
32
+ ```bash
33
+ % ROUNDABOUT=1 rails test:system
35
34
  ```
36
35
 
36
+ All page transitions via capybara will be recorded, then woven into a diagram.
37
37
 
38
- ## Usage
38
+ To see the generated diagram, just browse at your `http://localhost:3000/roundabout`.
39
+ You can also download a png image version and a PDF version from that page.
39
40
 
40
- Firstly, run the whole test (I suppose parallel spec is not supported ATM)
41
41
 
42
- ```bash
43
- % rake spec
44
- ```
42
+ ## Example
45
43
 
46
- Then browse at your `http://localhost:3000/roundabout`
44
+ The image shown at the very top of this documentation was generated from [Redmine](https://github.com/redmine/redmine) project's codebase.
47
45
 
48
46
 
49
47
  ## Contributing
50
48
 
51
- 1. Fork it
52
- 2. Create your feature branch (`git checkout -b my-new-feature`)
53
- 3. Commit your changes (`git commit -am 'Add some feature'`)
54
- 4. Push to the branch (`git push origin my-new-feature`)
55
- 5. Create new Pull Request
49
+ Send me a PR with a patch.
56
50
 
57
51
 
58
52
  ## Team
data/Rakefile CHANGED
@@ -1 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = 'test/**/*_test.rb'
6
+ t.libs << 'test'
7
+ end
8
+
9
+ task default: :test
@@ -1,3 +1,2 @@
1
- //= require jquery
2
1
  //= require raphael-min
3
2
  //= require_tree .
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Roundabout
2
- class ApplicationController < ActionController::Base
4
+ class ApplicationController < ::ActionController::Base
3
5
  protect_from_forgery
4
6
  end
5
7
  end
@@ -1,4 +1,5 @@
1
- require 'pp'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'roundabout/application_controller'
3
4
  require 'graphviz'
4
5
 
@@ -35,8 +36,9 @@ module Roundabout
35
36
  end
36
37
  format.html do
37
38
  graph = GraphViz.parse_string(viz.output(dot: String))
38
- @nodes, @edges = graph.each_node.values, graph.each_edge.map {|e| e[:pos].source.split(' ').take(2).reverse << e[:color].source }
39
- @graph_width, @graph_height = graph.graph.data['bb'].to_s.scan(/.*?(\d+),(\d+)"/).first
39
+ @nodes = graph.each_node.values
40
+ @edges = graph.each_edge.map {|e| e[:pos].source.split(' ').take(2).map {|s| s.sub(/^e,/, '') }.reverse << e[:color].source << e.node_one << e.node_two }
41
+ @graph_width, @graph_height = graph.graph.data['bb'].to_ruby.last(2)
40
42
  end
41
43
  end
42
44
  else
@@ -1,5 +1,5 @@
1
1
  <div id="roundabout-container" style="width: <%= @graph_width %>px; height: <%= @graph_height %>px;">
2
- <div id="roundabout" data-svg-width="<%= @graph_width %>" data-svg-height="<%= @graph_height %>">
2
+ <div id="roundabout">
3
3
  <% @nodes.each.with_index(1) do |node, i| %>
4
4
  <% width, height = BigDecimal.new(node[:width].source) * 72, BigDecimal.new(node[:height].source) * 72 %>
5
5
  <div id="roundabout-node-<%= i %>" class="node" style="top: <%= node[:pos].point[1] - height / 2 %>px; left: <%= node[:pos].point[0] - width / 2 %>px; width: <%= width %>px; height: <%= height %>px;">
@@ -7,7 +7,6 @@
7
7
  <div class="node-name"><span class="controller_name"><%= node_controller_name %></span><span class="controller_name"><%= node_action_name %></span></div>
8
8
  </div>
9
9
  <% end %>
10
- <script>window.raw_edges = '<%= @edges.to_json.html_safe %>';</script>
11
10
  </div>
12
11
  </div>
13
12
  <div id="downloads">
@@ -26,3 +25,18 @@
26
25
  </li>
27
26
  </ul>
28
27
  </div>
28
+ <script>
29
+ (function () {
30
+ window.paper = Raphael(0, 0, <%= @graph_width %>, <%= @graph_height %>);
31
+
32
+ <%= @edges.to_json.html_safe %>.forEach(function(e) {
33
+ var path;
34
+ if (e[3] == e[4]) {
35
+ path = ['M', e[0], 'A', 10, 10, 180, 0, 0, e[1]]
36
+ } else {
37
+ path = ['M', e[0], e[1]]
38
+ }
39
+ window.paper.path(path).attr({'stroke-width': 2, stroke: e[2], 'arrow-end': 'classic-wide-long'})
40
+ })
41
+ })();
42
+ </script>
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Roundabout::Engine.routes.draw do
2
- get '/(.:format)' => 'roundabout#index', as: 'root'
4
+ get '/(.:format)' => 'roundabout#index'
3
5
  end
Binary file
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'roundabout/version'
2
4
  require 'roundabout/railtie'
3
5
  require 'roundabout/recorder'
4
- require 'roundabout/monkey/capybara'
5
6
 
6
7
  module Roundabout
7
8
  def self.record_transition(from, to, method, type)
@@ -20,4 +21,9 @@ module Roundabout
20
21
  h = Rails.application.routes.recognize_path(path, method: method)
21
22
  "#{h[:controller]}##{h[:action]}"
22
23
  end
24
+
25
+ def self.save_results
26
+ transitions = compile_page_transitions
27
+ Rails.root.join('tmp/roundabout.json').open('w') {|f| f.write transitions.to_json} unless transitions.empty?
28
+ end
23
29
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Roundabout
2
4
  class Engine < ::Rails::Engine
3
5
  isolate_namespace Roundabout
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Minitest.after_run do
4
+ Roundabout.save_results
5
+ end
@@ -1,14 +1,21 @@
1
- module ActionController
2
- module Redirecting
3
- def redirect_to_with_recording(options = {}, response_status = {})
4
- redirect_to_without_recording options, response_status
5
- begin
6
- h = @_request.env['action_dispatch.request.path_parameters'].with_indifferent_access
7
- Roundabout.record_transition "#{h[:controller]}##{h[:action]}", Roundabout.normalize_url(self.location), :get, :redirect
8
- rescue => e
9
- p e
1
+ # frozen_string_literal: true
2
+
3
+ module Roundabout
4
+ module ActionController
5
+ module Redirecting
6
+ def redirect_to(*)
7
+ ret = super
8
+
9
+ begin
10
+ h = @_request.env['action_dispatch.request.path_parameters'].with_indifferent_access
11
+ Roundabout.record_transition "#{h[:controller]}##{h[:action]}", Roundabout.normalize_url(self.location), :get, :redirect
12
+ rescue => e
13
+ p e
14
+ end
15
+ ret
10
16
  end
11
17
  end
12
- alias_method_chain :redirect_to, :recording
13
18
  end
14
19
  end
20
+
21
+ ::ActionController::Base.prepend Roundabout::ActionController::Redirecting
@@ -1,28 +1,52 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'capybara'
2
4
 
3
- class Capybara::RackTest::Browser
4
- def follow_with_recording(method, path, attributes = {})
5
- return if path.gsub(/^#{request_path}/, '').start_with?('#')
6
- begin
7
- Roundabout.record_transition Roundabout.normalize_url(URI.parse(driver.current_url).path), Roundabout.normalize_url(path, method), method, :link
8
- rescue => e
9
- p e
10
- end
5
+ module Roundabout
6
+ module Capybara
7
+ module Node
8
+ module Element
9
+ def click(*)
10
+ path_from = session.current_path
11
+ case tag_name
12
+ when 'input'
13
+ type = :form
14
+ begin
15
+ form = ancestor 'form'
16
+ path_to = form[:action]
17
+ method = form[:method]
18
+ begin
19
+ if (hidden = form.first('input[type=hidden][name=_method]', visible: false))
20
+ method = hidden.value
21
+ end
22
+ rescue ::Capybara::ExpectationNotMet
23
+ end
24
+ rescue ::Capybara::ElementNotFound
25
+ end
26
+ when 'a'
27
+ if (method = self[:'data-method'])
28
+ type = :form
29
+ else
30
+ method = :get
31
+ type = :link
32
+ end
33
+ path_to = self[:href]
34
+ end
11
35
 
12
- follow_without_recording method, path, attributes
13
- end
14
- alias_method_chain :follow, :recording
36
+ ret = super
15
37
 
16
- def submit_with_recording(method, path, attributes)
17
- begin
18
- p = (path.nil? || path.empty?) ? request_path : path
19
- m = attributes['_method'] || method
20
- Roundabout.record_transition Roundabout.normalize_url(URI.parse(driver.current_url).path), Roundabout.normalize_url(p, m), m, :form
21
- rescue => e
22
- p e
38
+ if path_from && path_to && (path_from != path_to)
39
+ begin
40
+ Roundabout.record_transition Roundabout.normalize_url(path_from), Roundabout.normalize_url(path_to, method), method.to_sym, type
41
+ rescue => e
42
+ p e
43
+ end
44
+ end
45
+ ret
46
+ end
47
+ end
23
48
  end
24
-
25
- submit_without_recording method, path, attributes
26
49
  end
27
- alias_method_chain :submit, :recording
28
50
  end
51
+
52
+ ::Capybara::Node::Element.prepend Roundabout::Capybara::Node::Element
@@ -1,16 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails'
2
4
  require 'roundabout/engine'
3
5
 
4
6
  module Roundabout
5
7
  class Railtie < ::Rails::Railtie #:nodoc:
6
- initializer 'roundabout' do |app|
7
- ActiveSupport.on_load(:action_controller) do
8
- require 'roundabout/monkey/action_controller'
8
+ initializer 'roundabout' do
9
+ if ENV['ROUNDABOUT']
10
+ if Rails::VERSION::MAJOR >= 5
11
+ ActiveSupport.on_load :action_dispatch_integration_test do
12
+ require 'roundabout/minitest' if defined? Minitest
13
+ end
14
+ else
15
+ ActiveSupport.on_load :active_support_test_case do
16
+ require 'roundabout/minitest' if defined? Minitest
17
+ end
18
+ end
19
+
20
+ ActiveSupport.on_load :action_controller do
21
+ require 'roundabout/monkey/action_controller'
22
+ end
23
+
24
+ config.after_initialize do |app|
25
+ require 'roundabout/monkey/capybara'
26
+ require 'roundabout/rspec' if defined? RSpec
27
+ require 'roundabout/test-unit' if defined? TestUnitRails
28
+ end
9
29
  end
10
- ActiveSupport.on_load(:after_initialize) do
11
- if Rails.env.development?
12
- Rails.application.routes.append do
13
- mount Roundabout::Engine, :at => '/roundabout'
30
+
31
+ if Rails.env.development?
32
+ config.after_initialize do |app|
33
+ app.routes.append do
34
+ mount Roundabout::Engine => '/roundabout'
14
35
  end
15
36
  end
16
37
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  module Roundabout
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.configure do |config|
2
4
  config.after :suite do
3
- transitions = Roundabout.compile_page_transitions
4
- Rails.root.join('tmp/roundabout.json').open('w') {|f| f.write transitions.to_json} unless transitions.empty?
5
+ Roundabout.save_results
5
6
  end
6
7
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ at_exit do
4
+ Roundabout.save_results
5
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Roundabout
2
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
3
5
  end
@@ -18,6 +18,16 @@ Gem::Specification.new do |gem|
18
18
  gem.require_paths = ["lib"]
19
19
 
20
20
  gem.add_runtime_dependency 'capybara', ['>= 1.0.0']
21
- gem.add_runtime_dependency 'rspec', ['>= 2.0.0']
22
21
  gem.add_runtime_dependency 'ruby-graphviz', ['>= 1.0.0']
22
+
23
+ gem.add_development_dependency 'rails'
24
+ gem.add_development_dependency 'minitest'
25
+ gem.add_development_dependency 'sqlite3'
26
+ gem.add_development_dependency 'selenium-webdriver'
27
+ gem.add_development_dependency 'chromedriver-helper'
28
+ gem.add_development_dependency 'puma'
29
+ gem.add_development_dependency 'sass-rails'
30
+ gem.add_development_dependency 'uglifier'
31
+ gem.add_development_dependency 'sprockets-rails'
32
+ gem.add_development_dependency 'coffee-rails'
23
33
  end
@@ -0,0 +1,14 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5
+ // vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require rails-ujs
14
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6
+ * vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */