reverse_coverage 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +56 -0
  3. data/.rubocop.yml +19 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE +21 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +70 -0
  8. data/Rakefile +23 -0
  9. data/lib/reverse_coverage.rb +9 -0
  10. data/lib/reverse_coverage/formatters/html/assets/javascripts/application.js +204 -0
  11. data/lib/reverse_coverage/formatters/html/assets/javascripts/libraries/jquery-1.6.2.min.js +18 -0
  12. data/lib/reverse_coverage/formatters/html/assets/javascripts/plugins/highlight.pack.js +1 -0
  13. data/lib/reverse_coverage/formatters/html/assets/javascripts/plugins/jquery.colorbox.js +1090 -0
  14. data/lib/reverse_coverage/formatters/html/assets/javascripts/plugins/jquery.dataTables.min.js +152 -0
  15. data/lib/reverse_coverage/formatters/html/assets/javascripts/plugins/jquery.timeago.js +141 -0
  16. data/lib/reverse_coverage/formatters/html/assets/javascripts/plugins/jquery.url.js +174 -0
  17. data/lib/reverse_coverage/formatters/html/assets/javascripts/settings.js +1 -0
  18. data/lib/reverse_coverage/formatters/html/assets/javascripts/treeview.js +261 -0
  19. data/lib/reverse_coverage/formatters/html/assets/stylesheets/application.css +4 -0
  20. data/lib/reverse_coverage/formatters/html/assets/stylesheets/plugins/highlight.css +129 -0
  21. data/lib/reverse_coverage/formatters/html/assets/stylesheets/plugins/jquery-ui-1.8.4.custom.css +295 -0
  22. data/lib/reverse_coverage/formatters/html/assets/stylesheets/plugins/jquery.colorbox.css +52 -0
  23. data/lib/reverse_coverage/formatters/html/assets/stylesheets/reset.css +103 -0
  24. data/lib/reverse_coverage/formatters/html/assets/stylesheets/screen.css.sass +249 -0
  25. data/lib/reverse_coverage/formatters/html/assets/stylesheets/treeview.css +12 -0
  26. data/lib/reverse_coverage/formatters/html/formatter.rb +141 -0
  27. data/lib/reverse_coverage/formatters/html/public/application.css +841 -0
  28. data/lib/reverse_coverage/formatters/html/public/application.js +2045 -0
  29. data/lib/reverse_coverage/formatters/html/public/colorbox/border.png +0 -0
  30. data/lib/reverse_coverage/formatters/html/public/colorbox/controls.png +0 -0
  31. data/lib/reverse_coverage/formatters/html/public/colorbox/loading.gif +0 -0
  32. data/lib/reverse_coverage/formatters/html/public/colorbox/loading_background.png +0 -0
  33. data/lib/reverse_coverage/formatters/html/public/favicon.png +0 -0
  34. data/lib/reverse_coverage/formatters/html/public/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  35. data/lib/reverse_coverage/formatters/html/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  36. data/lib/reverse_coverage/formatters/html/public/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  37. data/lib/reverse_coverage/formatters/html/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  38. data/lib/reverse_coverage/formatters/html/public/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  39. data/lib/reverse_coverage/formatters/html/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  40. data/lib/reverse_coverage/formatters/html/public/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  41. data/lib/reverse_coverage/formatters/html/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  42. data/lib/reverse_coverage/formatters/html/public/images/ui-icons_222222_256x240.png +0 -0
  43. data/lib/reverse_coverage/formatters/html/public/images/ui-icons_2e83ff_256x240.png +0 -0
  44. data/lib/reverse_coverage/formatters/html/public/images/ui-icons_454545_256x240.png +0 -0
  45. data/lib/reverse_coverage/formatters/html/public/images/ui-icons_888888_256x240.png +0 -0
  46. data/lib/reverse_coverage/formatters/html/public/images/ui-icons_cd0a0a_256x240.png +0 -0
  47. data/lib/reverse_coverage/formatters/html/public/loading.gif +0 -0
  48. data/lib/reverse_coverage/formatters/html/public/magnify.png +0 -0
  49. data/lib/reverse_coverage/formatters/html/public/settings.js +2 -0
  50. data/lib/reverse_coverage/formatters/html/views/file_list.erb +24 -0
  51. data/lib/reverse_coverage/formatters/html/views/layout.erb +39 -0
  52. data/lib/reverse_coverage/formatters/html/views/source_file.erb +24 -0
  53. data/lib/reverse_coverage/main.rb +107 -0
  54. data/lib/reverse_coverage/version.rb +5 -0
  55. data/reverse_coverage.gemspec +36 -0
  56. metadata +240 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8289c5962409f6180efde37d89d711d8eaa9ef2b
4
+ data.tar.gz: 462039269712ca381a1cd9441666fbb341666ab4
5
+ SHA512:
6
+ metadata.gz: 3d1c17a176cebda761722d7d572063e941ff4565055a3c9b97545a0fcdf5b920dc611572c02d39059de9f1d9d247717ec564ea2e4ea0a3cfb8a79b9fe8f336bf
7
+ data.tar.gz: '084b29ff09e618bdf9ff28af26e8a7d04332a5b409a43b5d46f6f00a0544824f395ce58a1336ef106b89948a2d7252e654b1755b6704f76abd75e6010f47e973'
@@ -0,0 +1,56 @@
1
+ *.rbc
2
+ capybara-*.html
3
+ .rspec
4
+ .rspec_status
5
+ .rubocop-*
6
+ /log
7
+ /tmp
8
+ /db/*.sqlite3
9
+ /db/*.sqlite3-journal
10
+ /public/system
11
+ /coverage/
12
+ /spec/tmp
13
+ *.orig
14
+ rerun.txt
15
+ pickle-email-*.html
16
+
17
+ # TODO Comment out this rule if you are OK with secrets being uploaded to the repo
18
+ config/initializers/secret_token.rb
19
+ config/master.key
20
+
21
+ # Only include if you have production secrets in this file, which is no longer a Rails default
22
+ # config/secrets.yml
23
+
24
+ # dotenv
25
+ # TODO Comment out this rule if environment variables can be committed
26
+ .env
27
+
28
+ ## Environment normalization:
29
+ /.bundle
30
+ /vendor/bundle
31
+
32
+ # these should all be checked in to normalize the environment:
33
+ # Gemfile.lock, .ruby-version, .ruby-gemset
34
+ Gemfile.lock
35
+
36
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
37
+ .rvmrc
38
+
39
+ # if using bower-rails ignore default bower_components path bower.json files
40
+ /vendor/assets/bower_components
41
+ *.bowerrc
42
+ bower.json
43
+
44
+ # Ignore pow environment settings
45
+ .powenv
46
+
47
+ # Ignore Byebug command history file.
48
+ .byebug_history
49
+
50
+ # Ignore node_modules
51
+ node_modules/
52
+
53
+ # Others
54
+ spec/faked_project/tmp/reverse_coverage.yml
55
+
56
+ tmp/
@@ -0,0 +1,19 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ inherit_from:
5
+ - https://relaxed.ruby.style/rubocop.yml
6
+
7
+ Metrics/BlockLength:
8
+ Exclude:
9
+ - config/routes.rb
10
+ - db/seeds/**/*
11
+ - spec/**/*
12
+
13
+ RSpec/MultipleExpectations:
14
+ Max: 5
15
+ # Default is 1
16
+
17
+ RSpec/ExampleLength:
18
+ Max: 20
19
+ # Default is 5
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in reverse_coverage.gemspec
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Nebulab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Nebulab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,70 @@
1
+ [![Gem Version](https://badge.fury.io/rb/reverse_coverage.svg)](https://badge.fury.io/rb/reverse_coverage)
2
+
3
+ # ReverseCoverage
4
+
5
+ The goal of this component is to find what RSpec examples "cover" one or more lines of code of a Ruby project.
6
+
7
+ This is useful for example when you override methods of a parent product and you want to check what/how the specs treat the original lines.
8
+
9
+ ## Installation
10
+
11
+ Add `gem 'reverse_coverage'` to your application's Gemfile and execute `bundle`.
12
+
13
+ Put the following code under you specs configuration:
14
+
15
+ ```ruby
16
+ require 'reverse_coverage'
17
+
18
+ RSpec.configure do |config|
19
+ config.before(:suite) do
20
+ ReverseCoverage::Main.start
21
+ end
22
+
23
+ config.around do |e|
24
+ e.run
25
+ ReverseCoverage::Main.add(e)
26
+ end
27
+
28
+ config.after(:suite) do
29
+ ReverseCoverage::Main.save_results
30
+ coverage_matrix = ReverseCoverage::Main.coverage_matrix
31
+ ReverseCoverage::Formatters::HTML::Formatter.new.format(coverage_matrix)
32
+ end
33
+ end
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ Run your specs, inspect the `'tmp/reverse_coverage.yml'` file or open `'tmp/index.html'` file.
39
+
40
+ To configure the base URL used to open a specific spec edit the generated `'tmp/settings.js'` file. Without changes, the default behavior is to open files locally.
41
+
42
+ ## Options
43
+
44
+ - `file_filter`: allows to set a lambda to filter what files belongs to the project. Example (to add before `ReverseCoverage::Main.start` line): `ReverseCoverage::Main.config[:file_filter] = ->(file_path) { file_path.include? 'faked_project' }`
45
+
46
+ ## Testing
47
+
48
+ Execute `bundle exec rspec` on the component root path, specs are based on an internal fake project.
49
+
50
+ ## HTML output screenshot
51
+
52
+ Here it is a screenshot of the generated HTML interface:
53
+
54
+ ![Reverse Coverage screenshot](extra/screenshot.jpg)
55
+
56
+ ## Contributing
57
+
58
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nebulab/reverse_coverage. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
59
+
60
+ ## License
61
+
62
+ ReverseCoverage is copyright © 2019 [Nebulab](http://nebulab.it/). It is free software, and may be redistributed under the terms specified in the [license](LICENSE.txt).
63
+
64
+ ## About
65
+
66
+ ![Nebulab](http://nebulab.it/assets/images/public/logo.svg)
67
+
68
+ ReverseCoverage is funded and maintained by the [Nebulab](http://nebulab.it/) team.
69
+
70
+ We firmly believe in the power of open-source. [Contact us](http://nebulab.it/contact-us/) if you like our work and you need help with your project design or development.
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
9
+
10
+ namespace :assets do
11
+ desc "Compiles all assets"
12
+ task :compile do
13
+ puts "Compiling assets"
14
+ require "sprockets"
15
+ base_path = 'lib/reverse_coverage/formatters/html'
16
+ assets = Sprockets::Environment.new
17
+ assets.append_path "#{base_path}/assets/javascripts"
18
+ assets.append_path "#{base_path}/assets/stylesheets"
19
+ assets["application.js"].write_to("#{base_path}/public/application.js")
20
+ assets["settings.js"].write_to("#{base_path}/public/settings.js")
21
+ assets["application.css"].write_to("#{base_path}/public/application.css")
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'coverage'
4
+ require 'singleton'
5
+ require 'yaml'
6
+
7
+ require_relative 'reverse_coverage/version'
8
+ require_relative 'reverse_coverage/main'
9
+ require_relative 'reverse_coverage/formatters/html/formatter'
@@ -0,0 +1,204 @@
1
+ //= require_directory ./libraries/
2
+ //= require_directory ./plugins/
3
+ //= require treeview.js
4
+ //= require_self
5
+
6
+ $(document).ready(function() {
7
+ // Configuration for fancy sortable tables for source file groups
8
+ $('.file_list').dataTable({
9
+ "aaSorting": [[ 0, "asc" ]],
10
+ "bPaginate": false,
11
+ "bJQueryUI": true,
12
+ "aoColumns": [
13
+ null,
14
+ null,
15
+ null
16
+ ]
17
+ });
18
+
19
+ // Syntax highlight all files up front - deactivated
20
+ // $('.source_table pre code').each(function(i, e) {hljs.highlightBlock(e, ' ')});
21
+
22
+ // Syntax highlight source files on first toggle of the file view popup
23
+ $("a.src_link").click(function() {
24
+ // Get the source file element that corresponds to the clicked element
25
+ var source_table = $($(this).attr('href'));
26
+
27
+ // If not highlighted yet, do it!
28
+ if (!source_table.hasClass('highlighted')) {
29
+ source_table.find('pre code').each(function(i, e) {hljs.highlightBlock(e, ' ')});
30
+ source_table.addClass('highlighted');
31
+ };
32
+ });
33
+
34
+ var prev_anchor;
35
+ var curr_anchor;
36
+
37
+ // Set-up of popup for source file views
38
+ $("a.src_link").colorbox({
39
+ transition: "none",
40
+ inline: true,
41
+ opacity: 1,
42
+ width: "95%",
43
+ height: "95%",
44
+ onLoad: function() {
45
+ prev_anchor = curr_anchor ? curr_anchor : jQuery.url.attr('anchor');
46
+ curr_anchor = this.href.split('#')[1];
47
+ window.location.hash = curr_anchor;
48
+ },
49
+ onCleanup: function() {
50
+ if (prev_anchor && prev_anchor != curr_anchor) {
51
+ $('a[href="#'+prev_anchor+'"]').click();
52
+ curr_anchor = prev_anchor;
53
+ } else {
54
+ $('.group_tabs a:first').click();
55
+ prev_anchor = curr_anchor;
56
+ curr_anchor = "#_AllFiles";
57
+ }
58
+ window.location.hash = curr_anchor;
59
+ }
60
+ });
61
+
62
+ window.onpopstate = function(event){
63
+ if (location.hash.substring(0,2) == "#_") {
64
+ $.colorbox.close();
65
+ curr_anchor = jQuery.url.attr('anchor');
66
+ } else {
67
+ if ($('#colorbox').is(':hidden')) {
68
+ $('a.src_link[href="'+location.hash+'"]').colorbox({ open: true });
69
+ }
70
+ }
71
+ };
72
+
73
+ // Hide src files and file list container after load
74
+ $('.source_files').hide();
75
+ $('.file_list_container').hide();
76
+
77
+ // Add tabs based upon existing file_list_containers
78
+ $('.file_list_container h2').each(function(){
79
+ var container_id = $(this).parent().attr('id');
80
+ var group_name = $(this).find('.group_name').first().html();
81
+
82
+ $('.group_tabs').append('<li><a href="#' + container_id + '">' + group_name + '</a></li>');
83
+ });
84
+
85
+ $('.group_tabs a').each( function() {
86
+ $(this).addClass($(this).attr('href').replace('#', ''));
87
+ });
88
+
89
+ // Make sure tabs don't get ugly focus borders when active
90
+ $('.group_tabs a').live('focus', function() { $(this).blur(); });
91
+
92
+ var favicon_path = $('link[rel="icon"]').attr('href');
93
+ $('.group_tabs a').live('click', function(){
94
+ if (!$(this).parent().hasClass('active')) {
95
+ $('.group_tabs a').parent().removeClass('active');
96
+ $(this).parent().addClass('active');
97
+ $('.file_list_container').hide();
98
+ $(".file_list_container" + $(this).attr('href')).show();
99
+ window.location.href = window.location.href.split('#')[0] + $(this).attr('href').replace('#', '#_');
100
+
101
+ // Force favicon reload - otherwise the location change containing anchor would drop the favicon...
102
+ // Works only on firefox, but still... - Anyone know a better solution to force favicon on local file?
103
+ $('link[rel="shortcut icon"]').remove();
104
+ $('head').append('<link rel="shortcut icon" type="image/png" href="'+ favicon_path +'" />');
105
+ };
106
+ return false;
107
+ });
108
+
109
+ if (jQuery.url.attr('anchor')) {
110
+ var anchor = jQuery.url.attr('anchor')
111
+ if (anchor.length == 40) {
112
+ $('a.src_link[href=#' + anchor + ']').click();
113
+ } else {
114
+ $('.group_tabs a.'+anchor.replace('_', '')).click();
115
+ }
116
+ } else {
117
+ $('.group_tabs a:first').click();
118
+ };
119
+
120
+ $("abbr.timeago").timeago();
121
+ $('#loading').fadeOut();
122
+ $('#wrapper').show();
123
+ $('.dataTables_filter input').focus();
124
+
125
+ // https://stackoverflow.com/a/34430701
126
+ window.getAbsoluteUrl = function (base, relative) {
127
+ var hashPosition = base.indexOf('#');
128
+ if (hashPosition > 0){
129
+ base = base.slice(0, hashPosition);
130
+ }
131
+
132
+ var stack = base.split("/"),
133
+ parts = relative.split("/");
134
+ stack.pop();
135
+ for (var i=0; i<parts.length; i++) {
136
+ if (parts[i] == ".")
137
+ continue;
138
+ if (parts[i] == "..")
139
+ stack.pop();
140
+ else
141
+ stack.push(parts[i]);
142
+ }
143
+ return stack.join("/");
144
+ }
145
+
146
+ window.traverseTreeData = function(treeData, paths) {
147
+ if(paths.length == 0) { return []; }
148
+
149
+ const currentNode = paths[0];
150
+
151
+ let nodeIndex = treeData.findIndex(function(node) { return node.name == currentNode.name });
152
+ if(nodeIndex == -1) {
153
+ let node = {
154
+ name: currentNode.name,
155
+ example: currentNode.example,
156
+ expanded: true,
157
+ children: traverseTreeData([], paths.slice(1))
158
+ };
159
+ treeData.push(node);
160
+
161
+ } else {
162
+ let node = treeData[nodeIndex];
163
+ node.children = traverseTreeData(node.children, paths.slice(1));
164
+ node.expanded = (node.children.length > 1) ? false : true;
165
+ treeData[nodeIndex] = node;
166
+ }
167
+
168
+ return treeData;
169
+ };
170
+
171
+ $('.source_table.highlighted li.covered').live('click', function() {
172
+ const exampleRefs = $(this).children('.reverse_coverage')[0].dataset.exampleRefs.split(",");
173
+ if(!$(this).children('.reverse_coverage').is(':visible')){
174
+ let treeData = [];
175
+ const self = this;
176
+ exampleRefs.forEach(function(ref) {
177
+ const example = window.examples[ref]
178
+ const treeNodes = []
179
+ example["file_path"].split('/').slice(1).forEach(function(node_path){
180
+ treeNodes.push({ name: node_path })
181
+ });
182
+ treeNodes.push({ example: example, name: example['full_description'] });
183
+
184
+ treeData = traverseTreeData(treeData, treeNodes)
185
+ })
186
+
187
+ const domElement = self.querySelector('.reverse_coverage');
188
+
189
+ let tree = new TreeView(treeData, domElement);
190
+ tree.on('select', function (node) {
191
+ const example = node.data.example;
192
+ const url = getAbsoluteUrl('', `${window.base_url}${example.file_path}#L${example.line_number}`);
193
+ window.open(url, '_blank');
194
+ });
195
+ $(this).children('.reverse_coverage').show();
196
+ } else {
197
+ $(this).children('.reverse_coverage').html('');
198
+ $(this).children('.reverse_coverage').hide();
199
+ }
200
+ });
201
+ $('.source_table.highlighted li.covered .reverse_coverage').live('click', function(event) {
202
+ event.stopPropagation();
203
+ });
204
+ });