spec_views 1.1.1 → 2.0.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -5
  3. data/Rakefile +3 -1
  4. data/app/assets/config/spec_views_manifest.js +1 -0
  5. data/app/assets/javascripts/spec_views/diff.js +60 -0
  6. data/app/assets/javascripts/spec_views/jsdiff.js +1055 -0
  7. data/app/controllers/spec_views/views_controller.rb +18 -60
  8. data/app/models/spec_views/base_matcher.rb +64 -0
  9. data/app/models/spec_views/directory.rb +133 -0
  10. data/app/models/spec_views/html_matcher.rb +39 -0
  11. data/app/models/spec_views/http_response_extractor.rb +31 -0
  12. data/app/models/spec_views/mail_message_extractor.rb +30 -0
  13. data/app/models/spec_views/pdf_matcher.rb +49 -0
  14. data/app/views/layouts/spec_views.html.erb +261 -0
  15. data/app/views/spec_views/views/_actions.html.erb +11 -0
  16. data/app/views/spec_views/views/_directory_footer.html.erb +14 -0
  17. data/app/views/spec_views/views/compare.html.erb +11 -0
  18. data/app/views/spec_views/views/diff.html.erb +16 -0
  19. data/app/views/spec_views/views/index.html.erb +48 -0
  20. data/app/views/spec_views/views/preview.html.erb +32 -0
  21. data/config/routes.rb +3 -0
  22. data/lib/spec_views/configuration.rb +3 -2
  23. data/lib/spec_views/engine.rb +10 -0
  24. data/lib/spec_views/support.rb +56 -174
  25. data/lib/spec_views/version.rb +3 -1
  26. data/lib/spec_views.rb +3 -1
  27. data/lib/tasks/spec_views_tasks.rake +1 -0
  28. metadata +26 -15
  29. data/app/views/layouts/spec_views.html.haml +0 -200
  30. data/app/views/spec_views/views/compare.html.haml +0 -16
  31. data/app/views/spec_views/views/index.html.haml +0 -29
  32. data/app/views/spec_views/views/preview.html.haml +0 -19
@@ -0,0 +1,261 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html>
3
+ <head>
4
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
5
+ <title>Spec Views</title>
6
+ <meta charset="utf-8"/>
7
+ <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport"/>
8
+ <meta content="no-cache" name="turbolinks-cache-control"/>
9
+ <%= csrf_meta_tags %>
10
+ <%= csp_meta_tag %>
11
+ <style>
12
+ :root {
13
+ --dark-bg-light: #2A2D2E;
14
+ --dark-bg-lighter: #37373D;
15
+ --dark-text: #DADADA;
16
+
17
+ --accept-bg: #5fa285;
18
+ --accept-text: var(--dark-text);
19
+
20
+ --reject-bg: #d27575;
21
+ --reject-text: var(--dark-text);
22
+
23
+ --highlight: #0E639C;
24
+ --challenger: var(--highlight);
25
+ --outdated: var(--reject-bg);
26
+ }
27
+
28
+ * {
29
+ box-sizing: border-box;
30
+ margin: 0;
31
+ padding: 0;
32
+ }
33
+
34
+ body {
35
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
36
+ background: #252526;
37
+ color: #FFF;
38
+ margin: 0;
39
+ padding: 0;
40
+ }
41
+
42
+ .iframes {
43
+ position: relative;
44
+ z-index: 5;
45
+ display: flex;
46
+ justify-content: space-between;
47
+ align-items: stretch;
48
+ height: calc(100vh - 50px);
49
+ overflow: auto;
50
+ }
51
+
52
+ .iframes > * {
53
+ width: calc(50% - 5px);
54
+ height: 100%;
55
+ display: flex;
56
+ flex-direction: column;
57
+ justify-content: stretch;
58
+ }
59
+
60
+ .w-100 {
61
+ width: 100%;
62
+ }
63
+
64
+ iframe {
65
+ border: 0;
66
+ margin: 0;
67
+ padding: 0;
68
+ width: 100%;
69
+ flex-grow: 1;
70
+ }
71
+
72
+ h2 {
73
+ margin: 0;
74
+ padding: 4px 0;
75
+ text-align: center;
76
+ font-weight: 300;
77
+ text-transform: uppercase;
78
+ font-size: 12px;
79
+ line-height: 1;
80
+ }
81
+
82
+ .footer {
83
+ position: relative;
84
+ z-index: 10;
85
+ display: flex;
86
+ justify-content: space-between;
87
+ height: 50px;
88
+ box-shadow: 0px -1px 4px rgba(0, 0, 0, 0.65);
89
+ }
90
+
91
+ .info {
92
+ padding: 0 0 0 10px;
93
+ font-size: 14px;
94
+ display: flex;
95
+ align-items: center;
96
+ }
97
+ .info > div {
98
+ padding: 0.5rem 0.2rem;
99
+ }
100
+
101
+ .actions {
102
+ display: flex;
103
+ justify-content: flex-end;
104
+ padding: 2px 2rem 0 0;
105
+ }
106
+ .actions > * {
107
+ margin-left: 2rem;
108
+ }
109
+
110
+ form.button_to {
111
+ display: flex;
112
+ }
113
+
114
+ input[type=submit], .btn {
115
+ display: block;
116
+ padding: 0.5rem 0.5rem 0.4rem;
117
+ border: 0 solid var(--dark-bg-lighter);
118
+ border-width: 0 0 8px 0;
119
+ color: var(--dark-text);
120
+ border-radius: 0;
121
+ background: var(--dark-bg-light);
122
+ cursor: pointer;
123
+ font-size: 0.9rem;
124
+ line-height: 1.75;
125
+ min-width: 5rem;
126
+ text-align: center;
127
+ }
128
+ input[type=submit]:hover, .btn:hover {
129
+ background: var(--dark-bg-lighter);
130
+ }
131
+ input[type=submit].accept, .btn.accept {
132
+ border-color: var(--accept-bg);
133
+ color: var(--accept-text);
134
+ }
135
+ input[type=submit].reject, .btn.reject {
136
+ border-color: var(--reject-bg);
137
+ color: var(--reject-text);
138
+ }
139
+ input[type=submit].btn, .btn.btn {
140
+ border-color: var(--highlight);
141
+ }
142
+ input[type=submit].disabled, .btn.disabled {
143
+ opacity: 0.5;
144
+ cursor: default;
145
+ }
146
+ input[type=submit].disabled:hover, .btn.disabled:hover {
147
+ background: var(--dark-bg-light);
148
+ }
149
+
150
+ a {
151
+ color: var(--dark-text);
152
+ text-decoration: none;
153
+ }
154
+
155
+ .directories {
156
+ display: flex;
157
+ flex-direction: column;
158
+ align-items: center;
159
+ margin: 5rem auto 0;
160
+ list-style-type: none;
161
+ padding: 0;
162
+ padding-bottom: 2rem;
163
+ }
164
+ .directories > div {
165
+ margin-top: 1rem;
166
+ width: 58rem;
167
+ }
168
+ .directories > a {
169
+ display: flex;
170
+ flex: 1 0 auto;
171
+ align-items: stretch;
172
+ line-height: 1.5;
173
+ }
174
+ .directories > a:hover, .directories > a:focus {
175
+ background-color: var(--dark-bg-light);
176
+ }
177
+ .directories > a:active {
178
+ background-color: var(--dark-bg-lighter);
179
+ }
180
+ .directories > a.challenger > div:first-child {
181
+ border-color: var(--challenger);
182
+ text-align: center;
183
+ }
184
+ .directories > a.outdated > div:first-child {
185
+ border-color: var(--outdated);
186
+ text-align: center;
187
+ }
188
+ .directories > a > div {
189
+ padding: 0.5rem;
190
+ }
191
+ .directories > a > :first-child {
192
+ width: 8rem;
193
+ border: 0 solid transparent;
194
+ border-left-width: 1rem;
195
+ font-size: 0.75rem;
196
+ display: flex;
197
+ align-items: center;
198
+ }
199
+ .directories > a > :last-child {
200
+ display: flex;
201
+ flex: 1 0 auto;
202
+ width: calc(100vw - 8rem - 1rem);
203
+ align-items: center;
204
+ }
205
+ @media screen and (min-width: 1300px) {
206
+ .directories > a > :last-child {
207
+ width: 75vw;
208
+ }
209
+ }
210
+ @media screen and (min-width: 2500px) {
211
+ .directories > a > :last-child {
212
+ width: 65vw;
213
+ }
214
+ }
215
+ .directories > a > :last-child > :nth-child(1) {
216
+ flex-shrink: 0;
217
+ overflow: hidden;
218
+ text-overflow: ellipsis;
219
+ width: 15rem;
220
+ text-align: right;
221
+ }
222
+ .directories > a > :last-child > :nth-child(2) {
223
+ flex-shrink: 0;
224
+ padding: 0 1rem;
225
+ text-align: center;
226
+ width: 4rem;
227
+ }
228
+
229
+ #diff-settings {
230
+ box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.65);
231
+ margin: 0;
232
+ padding: 1rem;
233
+ }
234
+
235
+ #diff-settings span, #diff-settings label {
236
+ margin-right: 1rem;
237
+ }
238
+ #diff-settings span {
239
+ margin-right: 1rem;
240
+ }
241
+
242
+ #diff-challenger, #diff-champion {
243
+ display: none;
244
+ }
245
+
246
+ #diff-result {
247
+ margin-top: 1rem;
248
+ padding: 0 1rem;
249
+ }
250
+ #diff-result ins {
251
+ color: #9dd795;
252
+ }
253
+ #diff-result del {
254
+ color: #e39ca2;
255
+ }
256
+ </style>
257
+ </head>
258
+ <body>
259
+ <%= yield %>
260
+ </body>
261
+ </html>
@@ -0,0 +1,11 @@
1
+ <div class="actions">
2
+ <%= link_to 'Index', url_for(action: :index), class: 'index btn' %>
3
+ <% unless action_name == 'compare' %>
4
+ <%= link_to 'Compare', url_for(action: :compare), class: 'compare btn' %>
5
+ <% end %>
6
+ <% if action_name != 'diff' && diffable %>
7
+ <%= link_to 'Diff', url_for(action: :diff), class: 'diff btn' %>
8
+ <% end %>
9
+ <%= button_to 'Reject', url_for(action: :reject), class: 'reject' %>
10
+ <%= button_to 'Accept', url_for(action: :accept), class: 'accept' %>
11
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="footer">
2
+ <div class="info">
3
+ <div>
4
+ <%= directory.controller_name %>
5
+ </div>
6
+ <div>
7
+ <%= directory.method %>
8
+ </div>
9
+ <div>
10
+ <%= directory.description_tail %>
11
+ </div>
12
+ </div>
13
+ <%= render 'actions', diffable: !directory.binary? %>
14
+ </div>
@@ -0,0 +1,11 @@
1
+ <div class="iframes">
2
+ <div>
3
+ <h2>Champion</h2>
4
+ <iframe src="<%= url_for(action: :show) %>"></iframe>
5
+ </div>
6
+ <div>
7
+ <h2>Challenger</h2>
8
+ <iframe src="<%= url_for(action: :show, view: :challenger) %>"></iframe>
9
+ </div>
10
+ </div>
11
+ <%= render 'directory_footer', directory: @directory %>
@@ -0,0 +1,16 @@
1
+ <div class="iframes">
2
+ <div class="w-100">
3
+ <div id="diff-settings">
4
+ <span>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"><%= @champion %></div>
10
+ <div id="diff-challenger"><%= @challenger %></div>
11
+ <pre id="diff-result"></pre>
12
+ </div>
13
+ </div>
14
+ <%= render 'directory_footer', directory: @directory %>
15
+
16
+ <%= javascript_include_tag 'spec_views/diff' %>
@@ -0,0 +1,48 @@
1
+ <div class="iframes">
2
+ <div class="w-100">
3
+ <div class="directories">
4
+ <% @directories.each do |dir| %>
5
+ <% body = capture do %>
6
+ <div>
7
+ <div>
8
+ <%= dir.controller_name %>
9
+ </div>
10
+ <div>
11
+ <%= dir.method %>
12
+ </div>
13
+ <div>
14
+ <%= dir.description_tail %>
15
+ </div>
16
+ </div>
17
+ <% end %>
18
+ <% if dir.challenger? %>
19
+ <%= link_to({ action: :compare, id: dir }, class: 'challenger') do %>
20
+ <div>CHALLENGER</div>
21
+ <%= body %>
22
+ <% end %>
23
+ <% elsif dir.last_run < @latest_run %>
24
+ <%= link_to({ action: :preview, id: dir }, class: 'outdated') do %>
25
+ <div>OUTDATED</div>
26
+ <%= body %>
27
+ <% end %>
28
+ <% else %>
29
+ <%= link_to(action: :preview, id: dir) do %>
30
+ <div></div>
31
+ <%= body %>
32
+ <% end %>
33
+ <% end %>
34
+ <% end %>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <div class="footer">
39
+ <div class="info"></div>
40
+ <div class="actions">
41
+ <% if @directories.any?{ |dir| dir.last_run < @latest_run } %>
42
+ <%= button_to 'Remove Outdated', url_for(action: :destroy_outdated), method: :delete, class: 'reject' %>
43
+ <% end %>
44
+ <% if @directories.any?(&:challenger?) %>
45
+ <%= button_to 'Accept All', url_for(action: :accept_all), method: :post, class: 'accept' %>
46
+ <% end %>
47
+ </div>
48
+ </div>
@@ -0,0 +1,32 @@
1
+ <div class="iframes">
2
+ <div class="w-100">
3
+ <h2>Champion</h2>
4
+ <iframe src="<%= url_for(action: :show) %>"></iframe>
5
+ </div>
6
+ </div>
7
+ <div class="footer">
8
+ <div class="info">
9
+ <div>
10
+ <%= @directory.controller_name %>
11
+ </div>
12
+ <div>
13
+ <%= @directory.method %>
14
+ </div>
15
+ <div>
16
+ <%= @directory.description_tail %>
17
+ </div>
18
+ </div>
19
+ <div class="actions">
20
+ <%= link_to 'Index', url_for(action: :index), class: 'index btn' %>
21
+ <% if @previous %>
22
+ <%= link_to 'Previous', url_for(action: :preview, id: @previous), class: 'previous btn', disabled: true %>
23
+ <% else %>
24
+ <span class="previous btn disabled">Previous</span>
25
+ <% end %>
26
+ <% if @next %>
27
+ <%= link_to 'Next', url_for(action: :preview, id: @next), class: 'next btn' %>
28
+ <% else %>
29
+ <span class="previous btn disabled">Next</span>
30
+ <% end %>
31
+ </div>
32
+ </div>
data/config/routes.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Rails.application.routes.draw do
2
4
  namespace :spec_views do
3
5
  root to: redirect('spec_views/views')
@@ -9,6 +11,7 @@ Rails.application.routes.draw do
9
11
  end
10
12
  member do
11
13
  get :compare
14
+ get :diff
12
15
  get :preview
13
16
  post :accept
14
17
  post :reject
@@ -4,8 +4,9 @@ module SpecViews
4
4
  class Configuration < ActiveSupport::InheritableOptions
5
5
  def self.default
6
6
  new(
7
- directory: 'spec/fixtures/views'
7
+ directory: 'spec/fixtures/views',
8
+ ui_url: 'http://localhost:3000/spec_views'
8
9
  )
9
10
  end
10
11
  end
11
- end
12
+ end
@@ -1,7 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_views/configuration'
2
4
 
3
5
  module SpecViews
4
6
  class Engine < ::Rails::Engine
5
7
  config.spec_views = Configuration.default
8
+
9
+ initializer 'spec_views.assets.precompile' do |app|
10
+ app.config.assets.precompile += %w[spec_views/diff.js]
11
+ end
12
+
13
+ config.generators do |g|
14
+ g.test_framework :rspec
15
+ end
6
16
  end
7
17
  end
@@ -1,194 +1,76 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'timecop'
4
+
3
5
  module SpecViews
4
6
  module Support
5
- class SpecView
6
- attr_reader :body, :context, :example, :spec_name, :directory_path, :last_run_path,
7
- :time, :response
8
-
9
- class ReadError < StandardError
10
- def initialize(msg = 'My default message')
11
- super
12
- end
13
- end
14
-
15
- def initialize(context, example, response, time)
16
- @context = context
17
- @response = response
18
- @example = example
19
- @spec_name = example.full_description.strip
20
- .gsub(/[^0-9A-Za-z.\-]/, '_').gsub('__', '_')
21
- @spec_name = "#{@spec_name}__pdf" if pdf?
22
- @directory_path = Rails.root.join(Rails.configuration.spec_views.directory, spec_name)
23
- @last_run_path = directory_path.join('last_run.txt')
24
- @body = response.body
25
- @time = time
26
- end
27
-
28
- def write
29
- FileUtils.mkdir_p(directory_path)
30
- write_to_path(champion_path, sanitized_body)
31
- put_write_instructions
32
- end
33
-
34
- def write_challenger
35
- return unless changed?
36
-
37
- FileUtils.mkdir_p(directory_path)
38
- write_to_path(challenger_path, sanitized_body)
39
- end
40
-
41
- def delete_challenger
42
- FileUtils.rm_f(challenger_path) unless changed?
43
- end
44
-
45
- def write_last_run
46
- FileUtils.mkdir_p(directory_path)
47
- write_to_path(last_run_path, time)
48
- end
49
-
50
- def expect_eq
51
- pdf? ? expect_eq_pdf : expect_eq_text
52
- delete_challenger
53
- end
54
-
55
- def expect_eq_text
56
- context.expect(sanitized_body).to context.eq(read)
57
- end
58
-
59
- def expect_eq_pdf
60
- champion_md5 = nil
61
- begin
62
- champion_md5 = Digest::MD5.file(champion_path).hexdigest
63
- rescue Errno::ENOENT # rubocop:disable Lint/SuppressedException
64
- end
65
- current_md5 = Digest::MD5.hexdigest(sanitized_body)
66
- context.expect(current_md5).to context.eq(champion_md5), 'MD5s of PDF do not match'
67
- end
68
-
69
- def read
70
- File.read(champion_path)
71
- rescue Errno::ENOENT
72
- raise ReadError, "Cannot find view fixture #{champion_path.to_s.gsub(Rails.root.to_s, '')}\n" \
73
- "Create the file by adding the follwing to your spec:\n" \
74
- 'spec_view.write'
75
- end
76
-
77
- def changed?
78
- return pdf_changed? if pdf?
79
-
80
- begin
81
- champion = read
82
- rescue ReadError
83
- champion = nil
84
- end
85
- sanitized_body != champion
86
- end
87
-
88
- def pdf_changed?
89
- champion_md5 = Digest::MD5.file(champion_path).hexdigest
90
- current_md5 = Digest::MD5.hexdigest(sanitized_body)
91
- champion_md5 != current_md5
92
- rescue Errno::ENOENT
93
- true
94
- end
95
-
96
- private
97
-
98
- def champion_path
99
- pdf? ? directory_path.join('view.pdf') : directory_path.join('view.html')
100
- end
101
-
102
- def challenger_path
103
- pdf? ? directory_path.join('challenger.pdf') : directory_path.join('challenger.html')
104
- end
105
-
106
- def sanitized_body
107
- return remove_headers_from_pdf(body) if pdf?
108
-
109
- remove_pack_digests_from_body(
110
- remove_digests_from_body(body),
111
- )
112
- end
113
-
114
- def remove_digests_from_body(body)
115
- body.gsub(/(-[a-z0-9]{64})(\.css|\.js|\.ico|\.png|\.jpg|\.jpeg|\.svg|\.gif)/, '\2')
116
- end
117
-
118
- def remove_pack_digests_from_body(body)
119
- body.gsub(%r{(packs.*/js/[a-z0-9_]+)(-[a-z0-9]{20})(\.js)}, '\1\3')
120
- end
121
-
122
- def remove_headers_from_pdf(pdf)
123
- pdf
124
- .force_encoding("BINARY")
125
- .gsub(/^\/CreationDate \(.*\)$/, '')
126
- .gsub(/^\/ModDate \(.*\)$/, '')
127
- .gsub(/^\/ID \[<.+><.+>\]$/, '')
128
- end
129
-
130
- def put_write_instructions
131
- puts
132
- puts "\e[33mWarning:\e[0m Writing view fixture to #{champion_path.to_s.gsub(Rails.root.to_s, '')}"
133
- puts 'xdg-open "http://localhost:3100/spec_views/"'
134
- end
135
-
136
- def write_to_path(path, content)
137
- File.open(path.to_s, pdf? ? 'wb' : 'w') do |file|
138
- file.write(content)
139
- end
140
- end
141
-
142
- def pdf?
143
- response.content_type == 'application/pdf'
144
- end
145
- end
146
-
147
- def spec_view
148
- SpecView.new(self, @_spec_view_example, response, $_spec_view_time)
149
- end
150
-
151
7
  module SpecViewExample
152
- def render(description = nil, focus: nil, pending: nil, status: :ok, &block)
153
- context do # rubocop:disable RSpec/MissingExampleGroupArgument
154
- render_views
8
+ def it_renders(description = nil, focus: nil, pending: nil, status: :ok, &block)
9
+ context do
10
+ render_views if respond_to?(:render_views)
155
11
  options = {}
156
12
  options[:focus] = focus unless focus.nil?
157
13
  options[:pending] = pending unless pending.nil?
14
+ description = "renders #{description}" if description.is_a?(String)
158
15
  it(description, options) do
159
16
  instance_eval(&block)
160
- spec_view.write_last_run
161
- write = status.in?(
162
- [
163
- response.message.parameterize.underscore.to_sym,
164
- response.status,
165
- ],
166
- )
167
- spec_view.write_challenger if write
168
- spec_view.expect_eq
17
+ expect(response).to match_html_fixture.for_status(status)
169
18
  end
170
19
  end
171
20
  end
172
21
  end
22
+ end
23
+ end
173
24
 
174
- RSpec.configure do |c|
175
- c.extend SpecViewExample, type: :controller
176
- c.before(:suite) do |_example|
177
- $_spec_view_time = Time.zone.now
178
- end
179
- c.before(type: :controller) do |example|
180
- @_spec_view_example = example
181
- end
182
- c.before(:each, type: :controller) do
183
- Timecop.freeze(Time.zone.local(2024, 2, 29, 0o0, 17, 42))
184
- end
185
- c.after(:each, type: :controller) do
186
- Timecop.return
187
- end
25
+ RSpec.configure do |c|
26
+ c.before(:suite) do |_example|
27
+ $_spec_view_time = Time.zone.now # rubocop:disable Style/GlobalVars
28
+ end
29
+ %i[controller mailer request].each do |type|
30
+ c.extend SpecViews::Support::SpecViewExample, type: type
31
+ c.before(type: type) do |example|
32
+ @_spec_view_example = example
33
+ end
34
+ c.before(:each, type: type) do
35
+ Timecop.freeze(Time.zone.local(2024, 2, 29, 0o0, 17, 42))
36
+ end
37
+ c.after(:each, type: type) do
38
+ Timecop.return
188
39
  end
189
40
  end
190
41
  end
191
42
 
192
- RSpec.configure do |rspec|
193
- rspec.include SpecViews::Support, type: :controller
43
+
44
+ matchers = [
45
+ [:match_html_fixture, SpecViews::HtmlMatcher],
46
+ [:match_pdf_fixture, SpecViews::PdfMatcher]
47
+ ]
48
+
49
+ matchers.each do |matcher|
50
+ RSpec::Matchers.define matcher.first do |_expected|
51
+ chain :for_status do |status|
52
+ @status = status
53
+ end
54
+
55
+ match do |actual|
56
+ example = @matcher_execution_context.instance_variable_get('@_spec_view_example')
57
+ description = example.full_description
58
+ type = example.metadata[:type]
59
+ run_time = $_spec_view_time # rubocop:disable Style/GlobalVars
60
+ @status ||= :ok
61
+ @matcher = matcher.second.new(
62
+ actual,
63
+ description,
64
+ expected_status: @status,
65
+ run_time: run_time,
66
+ type: type
67
+ )
68
+ return @matcher.match?
69
+ end
70
+
71
+ failure_message do |_actual|
72
+ "#{@matcher.failure_message} " \
73
+ "Review the challenger: #{Rails.configuration.spec_views.ui_url}"
74
+ end
75
+ end
194
76
  end