spec_views 3.2.0 → 3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dde0c8db1ced1e6283cd8a14a721b755a03bc1d31cb0241a085c586bb0599c2e
4
- data.tar.gz: f69189b543569febf78eab2038da5663bbcaa177246a07f2942bdbec6ad88836
3
+ metadata.gz: eae6dff5e22673bd670708e1ff9c1855dcde1b7a669ed512edc83df789385e09
4
+ data.tar.gz: a96b241b47200e29e362388dede1e402325ae220bdc903a975614dd72c1fdf15
5
5
  SHA512:
6
- metadata.gz: 61248141337e1c7cbf2dc79797a2096fa33b7236b8b87c28a9f2aab5eefdc1e6527d9beb2ea7c6f953369c5210f24d4cc01f34aae0f45c208ab00a0fba95b9c7
7
- data.tar.gz: 55490980cf77efbeea95ff50d2af2b39c0902b74b00f3fcc97a5a67e028433c6622459d37ac01016a9b3abe9e6a53dc68343ab6b83607f466cebef44f1277bc1
6
+ metadata.gz: 8e8990413a8eed49d4a16bf39165c8910c426e6b08bbf864c015ba4d2fc4652a0ed648bf3e1a51f1c9cfaa53e2f75ca49e6be355c49480840efc2b5baff2b77c
7
+ data.tar.gz: ad2c9c6e96a4ce3170157ecd631b4825c25773fd5140b7cc5e2b6608c59e923d358bb6211732545c6a54813ab24a49d13d0e81813d088d0158f108794b256b02
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'diff/lcs'
4
+
3
5
  module SpecViews
4
6
  class ViewsController < ApplicationController
5
7
  skip_authorization_check if respond_to?(:skip_authorization_check)
@@ -18,6 +20,21 @@ module SpecViews
18
20
  end
19
21
  end
20
22
 
23
+ def batch
24
+ @change = BatchDiff.new(directories).biggest_change
25
+ redirect_to(url_for(action: :index), notice: 'No identical changes found') unless @change
26
+ end
27
+
28
+ def batch_accept
29
+ batch_diff = BatchDiff.new(directories)
30
+ batch_diff.accept_by_hash!(params[:hash])
31
+ if batch_diff.changes.size > 1
32
+ redirect_to(action: :batch)
33
+ else
34
+ redirect_to(action: :index)
35
+ end
36
+ end
37
+
21
38
  def show
22
39
  path = directory.champion_path
23
40
  path = directory.challenger_path if params[:view] == 'challenger'
@@ -83,8 +100,10 @@ module SpecViews
83
100
 
84
101
  def directories
85
102
  @directories ||= Pathname.new(directory_path).children.select(&:directory?).map do |path|
103
+ next unless File.file?("#{path}/meta.txt")
104
+
86
105
  SpecViews::Directory.new(path)
87
- end
106
+ end.compact
88
107
  latest_run = @directories.map(&:last_run).max
89
108
 
90
109
  @directories.sort_by! do |dir|
@@ -113,7 +132,7 @@ module SpecViews
113
132
 
114
133
  def accept_directory(dir)
115
134
  FileUtils.copy_file(dir.challenger_path, dir.champion_path)
116
- FileUtils.remove_file(dir.challenger_path)
135
+ dir.remove_challenger
117
136
  end
118
137
  end
119
138
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpecViews
4
+ class BatchDiff
5
+ attr_reader :directories
6
+
7
+ Change = Struct.new(:hash, :count) do
8
+ def patched_champion
9
+ Diff::LCS.patch!(champion, diff)
10
+ end
11
+
12
+ def champion
13
+ directory.champion_html
14
+ end
15
+
16
+ def add(directory, diff)
17
+ @pairs ||= []
18
+ @pairs.push([directory, diff])
19
+ self.count += 1
20
+ end
21
+
22
+ def directory
23
+ @pairs&.first&.first
24
+ end
25
+
26
+ def diff
27
+ @pairs&.first&.second
28
+ end
29
+
30
+ def accept!
31
+ @pairs.each do |directory, diff|
32
+ patched_champion = Diff::LCS.patch!(directory.champion_html, diff)
33
+ directory.write_champion(patched_champion)
34
+ directory.remove_challenger if patched_champion == directory.challenger_html
35
+ end
36
+ end
37
+ end
38
+
39
+ def initialize(directories)
40
+ @directories = directories.select(&:challenger?).select(&:champion?).reject(&:binary?)
41
+ end
42
+
43
+ def changes
44
+ @changes ||= begin
45
+ map = {}
46
+ @directories.each do |directory|
47
+ diffs = Diff::LCS.diff(directory.champion_html, directory.challenger_html)
48
+ diffs.each do |diff|
49
+ raw_diff = diff.map do |change|
50
+ element = change.element
51
+ element = '\r' if element == "\r"
52
+ element = '\n' if element == "\n"
53
+ [change.action, element]
54
+ end
55
+ diff_hash = raw_diff.map(&:join).join
56
+ map[diff_hash] ||= Change.new(diff_hash, 0)
57
+ map[diff_hash].add(directory, diff)
58
+ end
59
+ end
60
+ map.values.filter { |change| change.count > 1 }
61
+ end
62
+ end
63
+
64
+ def biggest_change
65
+ changes.max_by(&:count)
66
+ end
67
+
68
+ def accept_by_hash!(hash)
69
+ changes.select { |change| change.hash == hash }.map(&:accept!)
70
+ end
71
+
72
+ private
73
+
74
+ def get_view(path, html_safe: true)
75
+ content = File.read(path)
76
+ content = content.html_safe if html_safe
77
+ content
78
+ rescue Errno::ENOENT
79
+ ''
80
+ end
81
+ end
82
+ end
@@ -5,7 +5,7 @@ module SpecViews
5
5
  attr_reader :path
6
6
 
7
7
  def self.for_description(description, content_type: :html)
8
- dir_name = description.strip.gsub(/[^0-9A-Za-z.\-]/, '_').gsub('__', '_')
8
+ dir_name = description.strip.gsub(/[^0-9A-Za-z.-]/, '_').gsub('__', '_')
9
9
  new(Rails.root.join(Rails.configuration.spec_views.directory, dir_name), content_type)
10
10
  end
11
11
 
@@ -46,12 +46,21 @@ module SpecViews
46
46
  path.join("view.#{file_extension}")
47
47
  end
48
48
 
49
+ def champion?
50
+ File.file?(champion_path)
51
+ end
52
+
49
53
  def champion_html
50
54
  File.read(champion_path)
51
55
  rescue Errno::ENOENT
52
56
  nil
53
57
  end
54
58
 
59
+ def write_champion(content)
60
+ FileUtils.mkdir_p(path)
61
+ File.open(champion_path, binary? ? 'wb' : 'w') { |f| f.write(content) }
62
+ end
63
+
55
64
  def challenger_path
56
65
  path.join("challenger.#{file_extension}")
57
66
  end
@@ -60,11 +69,21 @@ module SpecViews
60
69
  File.file?(challenger_path)
61
70
  end
62
71
 
72
+ def challenger_html
73
+ File.read(challenger_path)
74
+ rescue Errno::ENOENT
75
+ nil
76
+ end
77
+
63
78
  def write_challenger(content)
64
79
  FileUtils.mkdir_p(path)
65
80
  File.open(challenger_path, binary? ? 'wb' : 'w') { |f| f.write(content) }
66
81
  end
67
82
 
83
+ def remove_challenger
84
+ FileUtils.remove_file(challenger_path)
85
+ end
86
+
68
87
  def meta_path
69
88
  path.join('meta.txt')
70
89
  end
@@ -25,7 +25,7 @@ module SpecViews
25
25
  def response_status_match?(response, expected_status)
26
26
  # Use "#{response.message}" to ensure local encoding
27
27
  response.status == expected_status ||
28
- "#{response.message}".parameterize.underscore == expected_status.to_s
28
+ response.message.to_s.parameterize.underscore == expected_status.to_s
29
29
  end
30
30
  end
31
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SpecViews
2
4
  class ViewSanitizer
3
5
  delegate :each, to: :@sanitizers
@@ -17,4 +19,4 @@ module SpecViews
17
19
  string
18
20
  end
19
21
  end
20
- end
22
+ end
@@ -39,6 +39,38 @@
39
39
  padding: 0;
40
40
  }
41
41
 
42
+ .flash {
43
+ position: absolute;
44
+ z-index: 10;
45
+ top: 1.5rem;
46
+ left: 0;
47
+ right: 0;
48
+ display: flex;
49
+ justify-content: center;
50
+ font-size: 0.9rem;
51
+ pointer-events: none;
52
+ animation: fade-out 1s 1;
53
+ animation-fill-mode: forwards;
54
+ animation-delay: 5s;
55
+ }
56
+ .flash > div {
57
+ padding: 0.5rem 1rem;
58
+ background-color: var(--dark-bg-lighter);
59
+ box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.65);
60
+ border: 0px solid transparent;
61
+ border-width: 0 6px;
62
+ border-color: var(--challenger);
63
+ pointer-events: all;
64
+ transition: opacity 0.25s ease-in-out;
65
+ }
66
+ .flash > div:hover {
67
+ opacity: 0;
68
+ }
69
+ @keyframes fade-out {
70
+ from { opacity :1; }
71
+ to { opacity :0; }
72
+ }
73
+
42
74
  .iframes {
43
75
  position: relative;
44
76
  z-index: 5;
@@ -256,6 +288,9 @@
256
288
  </style>
257
289
  </head>
258
290
  <body>
291
+ <% flash.each do |type, msg| %>
292
+ <div class="flash flash-<%= type %>"><div onclick="console.log(this.parent)"><%= msg %></div></div>
293
+ <% end %>
259
294
  <%= yield %>
260
295
  </body>
261
296
  </html>
@@ -0,0 +1,27 @@
1
+ <div class="iframes">
2
+ <div class="w-100">
3
+ <div id="diff-settings">
4
+ <span>Batch Diff</span>
5
+ <label><input type="radio" name="diff_type" value="diffChars" checked> Characters</label>
6
+ <label><input type="radio" name="diff_type" value="diffWords"> Words</label>
7
+ <label><input type="radio" name="diff_type" value="diffLines"> Lines</label>
8
+ </div>
9
+ <div id="diff-champion"><%= @change.champion %></div>
10
+ <div id="diff-challenger"><%= @change.patched_champion %></div>
11
+ <pre id="diff-result"></pre>
12
+ </div>
13
+ </div>
14
+ <div class="footer">
15
+ <div class="info">
16
+ <div>
17
+ <%= @change.count %> Views with this Changes
18
+ </div>
19
+ </div>
20
+ <div class="actions">
21
+ <%= link_to 'Index', url_for(action: :index), class: 'index btn' %>
22
+ <%= button_to "Accept Change for #{@change.count} Views", url_for(action: :batch_accept), { class: 'accept', params: { hash: @change.hash } } %>
23
+ </div>
24
+ </div>
25
+
26
+
27
+ <%= javascript_include_tag 'spec_views/diff' %>
@@ -38,6 +38,9 @@
38
38
  <div class="footer">
39
39
  <div class="info"></div>
40
40
  <div class="actions">
41
+ <% if @directories.any?(&:challenger?) %>
42
+ <%= link_to 'Batch Diff', url_for(action: :batch), class: 'diff btn' %>
43
+ <% end %>
41
44
  <% if @directories.any?{ |dir| dir.last_run < @latest_run } %>
42
45
  <%= button_to 'Remove Outdated', url_for(action: :destroy_outdated), method: :delete, class: 'reject' %>
43
46
  <% end %>
data/config/routes.rb CHANGED
@@ -6,8 +6,10 @@ Rails.application.routes.draw do
6
6
 
7
7
  resources :views, only: %i[index show] do
8
8
  collection do
9
+ get :batch
9
10
  delete :destroy_outdated
10
11
  post :accept_all
12
+ post :batch_accept
11
13
  end
12
14
  member do
13
15
  get :compare
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SpecViews
4
- VERSION = '3.2.0'
4
+ VERSION = '3.3.0'
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spec_views
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Gaul
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-02 00:00:00.000000000 Z
11
+ date: 2023-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: diff-lcs
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rails
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +107,7 @@ files:
93
107
  - app/assets/javascripts/spec_views/jsdiff.js
94
108
  - app/controllers/spec_views/views_controller.rb
95
109
  - app/models/spec_views/base_matcher.rb
110
+ - app/models/spec_views/batch_diff.rb
96
111
  - app/models/spec_views/capybara_session_extractor.rb
97
112
  - app/models/spec_views/directory.rb
98
113
  - app/models/spec_views/html_matcher.rb
@@ -103,6 +118,7 @@ files:
103
118
  - app/views/layouts/spec_views.html.erb
104
119
  - app/views/spec_views/views/_actions.html.erb
105
120
  - app/views/spec_views/views/_directory_footer.html.erb
121
+ - app/views/spec_views/views/batch.html.erb
106
122
  - app/views/spec_views/views/compare.html.erb
107
123
  - app/views/spec_views/views/diff.html.erb
108
124
  - app/views/spec_views/views/index.html.erb
@@ -133,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
149
  - !ruby/object:Gem::Version
134
150
  version: '0'
135
151
  requirements: []
136
- rubygems_version: 3.1.6
152
+ rubygems_version: 3.3.26
137
153
  signing_key:
138
154
  specification_version: 4
139
155
  summary: Render views from controller specs for comparision