roundabout 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ */