spec_views 1.1.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +111 -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 +35 -87
  8. data/app/models/spec_views/base_matcher.rb +66 -0
  9. data/app/models/spec_views/capybara_session_extractor.rb +30 -0
  10. data/app/models/spec_views/directory.rb +149 -0
  11. data/app/models/spec_views/html_matcher.rb +41 -0
  12. data/app/models/spec_views/http_response_extractor.rb +30 -0
  13. data/app/models/spec_views/mail_message_extractor.rb +31 -0
  14. data/app/models/spec_views/pdf_matcher.rb +53 -0
  15. data/app/models/spec_views/view_sanitizer.rb +20 -0
  16. data/app/views/layouts/spec_views.html.erb +261 -0
  17. data/app/views/spec_views/views/_actions.html.erb +11 -0
  18. data/app/views/spec_views/views/_directory_footer.html.erb +14 -0
  19. data/app/views/spec_views/views/compare.html.erb +11 -0
  20. data/app/views/spec_views/views/diff.html.erb +16 -0
  21. data/app/views/spec_views/views/index.html.erb +48 -0
  22. data/app/views/spec_views/views/preview.html.erb +32 -0
  23. data/config/routes.rb +3 -0
  24. data/lib/spec_views/configuration.rb +3 -2
  25. data/lib/spec_views/engine.rb +10 -0
  26. data/lib/spec_views/support.rb +61 -174
  27. data/lib/spec_views/version.rb +3 -1
  28. data/lib/spec_views.rb +3 -1
  29. data/lib/tasks/spec_views_tasks.rake +1 -0
  30. metadata +28 -15
  31. data/app/views/layouts/spec_views.html.haml +0 -200
  32. data/app/views/spec_views/views/compare.html.haml +0 -16
  33. data/app/views/spec_views/views/index.html.haml +0 -29
  34. data/app/views/spec_views/views/preview.html.haml +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c7f8a52383504378eddee951cba9a8fc6a47f255da78ed1a130eee0fa814ada
4
- data.tar.gz: c8fabfb166786ccb35a9ecb3b195851b04c73bea7b6fe3a0a823a3e72b963469
3
+ metadata.gz: af819d4fc2aa2487c81eb455030288533b00153f6fde05a5152211734c1625c1
4
+ data.tar.gz: c3b653db474cd69198cf0ccdd9be78a9c224be98a8802bcb0d4941553081e20c
5
5
  SHA512:
6
- metadata.gz: 838f2fc2cb3002b3e8b3d8274ba1f1e14ea0d3fcea5d54a6ff6bdf8e9919ab37f1c3f3b8a90594ff1911bb588230d513d78a444b838452f9115120eaf71d8dc8
7
- data.tar.gz: f3fb138e0899c4a448fe2b8c3121420b6b86162a6ef3f4b5b9a81fed764e6ca3aad89c311f66381aff4d4b4a5024f87fa5c01010fb92e8621007f80fc2cb1c37
6
+ metadata.gz: d5e1a78b8fe0cea35ab90985f35eeb512e12c262171a3279c1aead04f754aef40c29adf9e236d58d844a59f36f05fd8fea1f4ec16c6809b97e7f051dc15e978b
7
+ data.tar.gz: 296860d1ef38ce4136d7a5a61229aa5ece286c9aeaef409d0688eaf801e7e631fe0019bfd91370ebf077a029ecf2d63564d32a08e871ff17f837b2eb5560cb92
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # SpecViews
2
- Render views from controller specs for comparision.
2
+ Save views from request and controller specs for comparision.
3
3
 
4
4
  ## Installation
5
5
  Add this line to your application's Gemfile:
@@ -13,7 +13,7 @@ And this to RSpec's rails_helper.rb:
13
13
  require 'spec_views/support'
14
14
  ```
15
15
 
16
- And then execute:
16
+ Then execute:
17
17
  ```bash
18
18
  $ bundle
19
19
  ```
@@ -25,19 +25,125 @@ Ignore some generated files in your .gitignore:
25
25
  ```
26
26
 
27
27
  ## Usage
28
- Replace selected RSpec `it` methods with `render`:
28
+ Use the `match_html_fixture` matcher in your spec:
29
+
30
+ ```ruby
31
+ RSpec.describe "Articles", type: :request do
32
+ describe 'GET /articles' do
33
+ it 'renders the listing' do
34
+ get articles_path
35
+ expect(response).to match_html_fixture
36
+ end
37
+ end
38
+ end
39
+ ```
40
+
41
+ Run this spec to see it failing. Open SpecView UI [http://localhost:3000/spec_views](http://localhost:3000/spec_views) on Rails development server to accept the new view. When rerunning the spec it compares its rendering with the reference view. Use SpecView UI to review, accept or reject changed views.
42
+
43
+ ### view_sanitizer
44
+ Sometimes it is hard to remove dynamic elements like IDs and times. Use `view_sanitizer.gsub` to replace dynamic strings with fixed ones before comparing and saving the views automatically:
45
+
46
+ ```ruby
47
+ before do
48
+ view_sanitizer.gsub(%r{(users/)[0-9]+}, '\1ID')
49
+ view_sanitizer.gsub(/value="[0-9]+"/, 'value="ID"')
50
+ end
51
+ ```
52
+
53
+ `view_sanitizer.gsub` supports the same arguments as `String#gsub` including blocks.
54
+
55
+ ### it_renders
56
+ You can also use the shortcut to skip the matcher and the word "renders" from your description:
57
+
58
+ ```ruby
59
+ RSpec.describe "Articles", type: :request do
60
+ describe 'GET /articles' do
61
+ it_renders 'the listing' do
62
+ get articles_path
63
+ end
64
+ end
65
+ end
66
+ ```
67
+
68
+ ### Different response status
69
+ By default only status 200 responses are compared. Tell the matcher if your response has another one:
70
+
71
+ ```ruby
72
+ RSpec.describe "Articles", type: :request do
73
+ describe 'GET /articles' do
74
+ it 'renders the listing' do
75
+ get articles_path
76
+ expect(response).to match_html_fixture.for_status(:not_found) # or 404
77
+ end
78
+ end
79
+ end
80
+ ```
81
+
82
+ ### PDF matching
83
+ If your request responds with a PDF you can compare it as well:
84
+
85
+ ```ruby
86
+ RSpec.describe "Articles", type: :request do
87
+ describe 'GET /articles/1/download' do
88
+ it 'downloads as PDF' do
89
+ get article_path(1, format: :pdf)
90
+ expect(response).to match_pdf_fixture
91
+ end
92
+ end
93
+ end
94
+ ```
95
+
96
+ ### Mailer specs
97
+ Compare HTML mailers. `match_html_fixture` tries to find the HTML part of your mail automatically:
98
+
99
+ ```ruby
100
+ RSpec.describe NotificationsMailer, type: :mailer do
101
+ describe '#notify' do
102
+ let(:mail) { NotificationsMailer.signup }
103
+
104
+ it 'renders the body' do
105
+ expect(mail).to match_html_fixture
106
+ end
107
+ end
108
+ end
109
+ ```
110
+
111
+ ### Feature specs
112
+ Compare pages from feature specs:
113
+
114
+ ```ruby
115
+ RSpec.describe 'Articles', type: :feature do
116
+ it 'are shown as list' do
117
+ expect(page).to match_html_fixture
118
+ end
119
+ end
120
+ ```
121
+
122
+ ### Controller specs
123
+ Prefer request specs over controller specs. If you still want to use controller specs enable the `render_views` feature:
29
124
 
30
125
  ```ruby
31
126
  RSpec.describe HomeController, type: :controller do
32
127
  describe 'GET #show' do
33
- render 'homepage' do
128
+ render_views
129
+
130
+ it 'renders the homepage' do
34
131
  get :show
132
+ expect(response).to match_html_fixture
35
133
  end
36
134
  end
37
135
  end
38
136
  ```
39
137
 
40
- Run this spec to see it failing. Open SpecView UI [http://localhost:3000/spec_views](http://localhost:3000/spec_views) on Rails development server to accept the new view. When rerunning the spec it compares its rendering with the reference view. Use SpecView UI to review, accept or reject changed views.
138
+ The `it_renders` shortcuts enables `render_views` automatically.
139
+
140
+ ## Configuration
141
+ Configure SpecView by adding and modifying lines to `config/environments/test.rb` and `config/environments/development.rb`:
142
+
143
+ ```ruby
144
+ config.spec_views.directory = 'spec/fixtures/views'
145
+ config.spec_views.ui_url = 'http://localhost:3000/spec_views'
146
+ ```
41
147
 
42
148
  ## License
43
149
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -14,7 +16,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
16
  rdoc.rdoc_files.include('lib/**/*.rb')
15
17
  end
16
18
 
17
- APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
19
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
18
20
  load 'rails/tasks/engine.rake'
19
21
 
20
22
  load 'rails/tasks/statistics.rake'
@@ -0,0 +1 @@
1
+ //= link spec_views/diff.js
@@ -0,0 +1,60 @@
1
+ //= require spec_views/jsdiff
2
+
3
+ var a = document.getElementById('diff-champion');
4
+ var b = document.getElementById('diff-challenger');
5
+ var result = document.getElementById('diff-result');
6
+
7
+ function changed() {
8
+ var diff = JsDiff[window.diffType](a.textContent, b.textContent);
9
+ var fragment = document.createDocumentFragment();
10
+ for (var i=0; i < diff.length; i++) {
11
+
12
+ if (diff[i].added && diff[i + 1] && diff[i + 1].removed) {
13
+ var swap = diff[i];
14
+ diff[i] = diff[i + 1];
15
+ diff[i + 1] = swap;
16
+ }
17
+
18
+ var node;
19
+ if (diff[i].removed) {
20
+ node = document.createElement('del');
21
+ node.appendChild(document.createTextNode(diff[i].value));
22
+ } else if (diff[i].added) {
23
+ node = document.createElement('ins');
24
+ node.appendChild(document.createTextNode(diff[i].value));
25
+ } else {
26
+ node = document.createTextNode(diff[i].value);
27
+ }
28
+ fragment.appendChild(node);
29
+ }
30
+
31
+ result.textContent = '';
32
+ result.appendChild(fragment);
33
+ }
34
+
35
+ window.onload = function() {
36
+ onDiffTypeChange(document.querySelector('#diff-settings [name="diff_type"]:checked'));
37
+ changed();
38
+ };
39
+
40
+ a.onpaste = a.onchange =
41
+ b.onpaste = b.onchange = changed;
42
+
43
+ if ('oninput' in a) {
44
+ a.oninput = b.oninput = changed;
45
+ } else {
46
+ a.onkeyup = b.onkeyup = changed;
47
+ }
48
+
49
+ function onDiffTypeChange(radio) {
50
+ window.diffType = radio.value;
51
+ document.title = "Diff " + radio.value.slice(4);
52
+ }
53
+
54
+ var radio = document.getElementsByName('diff_type');
55
+ for (var i = 0; i < radio.length; i++) {
56
+ radio[i].onchange = function(e) {
57
+ onDiffTypeChange(e.target);
58
+ changed();
59
+ }
60
+ }