spec_views 1.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +111 -5
- data/Rakefile +3 -1
- data/app/assets/config/spec_views_manifest.js +1 -0
- data/app/assets/javascripts/spec_views/diff.js +60 -0
- data/app/assets/javascripts/spec_views/jsdiff.js +1055 -0
- data/app/controllers/spec_views/views_controller.rb +35 -87
- data/app/models/spec_views/base_matcher.rb +65 -0
- data/app/models/spec_views/capybara_session_extractor.rb +30 -0
- data/app/models/spec_views/directory.rb +140 -0
- data/app/models/spec_views/html_matcher.rb +41 -0
- data/app/models/spec_views/http_response_extractor.rb +30 -0
- data/app/models/spec_views/mail_message_extractor.rb +31 -0
- data/app/models/spec_views/pdf_matcher.rb +53 -0
- data/app/models/spec_views/view_sanitizer.rb +20 -0
- data/app/views/layouts/spec_views.html.erb +261 -0
- data/app/views/spec_views/views/_actions.html.erb +11 -0
- data/app/views/spec_views/views/_directory_footer.html.erb +14 -0
- data/app/views/spec_views/views/compare.html.erb +11 -0
- data/app/views/spec_views/views/diff.html.erb +16 -0
- data/app/views/spec_views/views/index.html.erb +48 -0
- data/app/views/spec_views/views/preview.html.erb +32 -0
- data/config/routes.rb +3 -0
- data/lib/spec_views/configuration.rb +3 -2
- data/lib/spec_views/engine.rb +10 -0
- data/lib/spec_views/support.rb +61 -170
- data/lib/spec_views/version.rb +3 -1
- data/lib/spec_views.rb +3 -1
- data/lib/tasks/spec_views_tasks.rake +1 -0
- metadata +28 -15
- data/app/views/layouts/spec_views.html.haml +0 -200
- data/app/views/spec_views/views/compare.html.haml +0 -16
- data/app/views/spec_views/views/index.html.haml +0 -29
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42a03f1d324852c51d56e4afdadadcc00e6449954917f396599df6c4568176d0
|
4
|
+
data.tar.gz: 466b8bddc2f1c78bed3bbe57370b26bc87f71ecf38e2476ccc46fa4237a5410a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48a9730de38ccc7927d852015fcb7c051e783a8a603cb665df17095a45799e882a1cc276d09b31177a2bc7ee93c8c86a455e5d0f24314e80ad19b77adcb36860
|
7
|
+
data.tar.gz: a837bcd14d1e95707830291dbd621c41bef508c3fc858854bc22c6142aee2170b24d29f991c055b457da173db0b6a94e0920aaeef87cca13b5d4f13724393238
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# SpecViews
|
2
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
+
}
|