still_life 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e8dc100759230e82f493b0bae7e3c6fc600f854ef6ff77a858037e92a662aaa7
4
+ data.tar.gz: f432cd88652963f9ffa70a249aa1554d3d0ce37ab5742d84538bcaebdca185e7
5
+ SHA512:
6
+ metadata.gz: 9cdcef43678b79532a812b2bd4d9006ad63fd75b27ed28ddf606bd7ff20e77f1860fb1dd3e13be8dd2f297d4ef140b4eaa7f69b2adf60f3e7c72c02ee6afb725
7
+ data.tar.gz: 812976cf80fa3b687dd11a3bafcc4642462544019726ad61fd96dc5ed9110724f6ef886b6d3016b09ea9bc35360c866c8ea4c5d60e365056168e1f4e64df8b48
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ Gemfile.lock
10
+ Gemfile.*.lock
11
+ log/
12
+ *.sqlite3
13
+ /test/dummy_app/tmp/cache/
14
+ /test/dummy_app/tmp/html/
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.7.0
7
+ before_install: gem install bundler -v 2.0.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in still_life.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Akira Matsuda
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,152 @@
1
+ # still_life
2
+
3
+ ## What's This?
4
+
5
+ still_life is a testing framework enhancements for test-unit, minitest, RSpec, and Capybara that records all HTML response body texts that are rendered during E2E or unit test executions.
6
+
7
+
8
+ ## So What?
9
+
10
+ You can compare actually rendered HTML results before and after any app updates.
11
+
12
+
13
+ ## For What?
14
+
15
+ By comparing all these HTML files that are processed before and after any kind of code change, you can make sure that you did not (or you did) introduce any new user-facing incompatibilities.
16
+ This may greatly help you for example, refactoring your app, replacing external libraries, or upgrading libraries.
17
+ My personal use case that made me gemifying still_life was that I wanted to make sure that [my own template engine](https://github.com/amatsuda/himl) renders the same HTML as the one that I was using.
18
+
19
+ But indeed, the real sweet spot of this tiny library is IMO "Rails upgrade".
20
+ In fact, The first original version of this tool was implemented as an RSpec monkeypatch while we were upgrading a huge Rails application from Rails 2 to Rails 3.
21
+
22
+
23
+ ## Installation
24
+
25
+ Bundle `still_life` gem to your Rails app's `:test` environment.
26
+
27
+ ```ruby
28
+ gem 'still_life', group: :test
29
+ ```
30
+
31
+
32
+ ## Usage
33
+
34
+ Run tests with an envvar `STILL_LIFE`. Then still_life creates some HTML files under `tmp/html/#{ENV['STILL_LIFE']}/` directory.
35
+ Each .html file is named from the location in your test code where the request was made.
36
+
37
+ For instance, if you run the tests against a simple scaffold app, the generated files will be like this:
38
+
39
+ ```sh
40
+ % STILL_LIFE=rails52 rails test:system test
41
+ % tree tmp/html
42
+ tmp/html
43
+ └── rails52
44
+ └── test
45
+ ├── controllers
46
+ │   ├── users_controller_test.rb-14.html
47
+ │   ├── users_controller_test.rb-20.html
48
+ │   ├── users_controller_test.rb-27.html
49
+ │   ├── users_controller_test.rb-32.html
50
+ │   ├── users_controller_test.rb-37.html
51
+ │   ├── users_controller_test.rb-43.html
52
+ │   └── users_controller_test.rb-9.html
53
+ └── system
54
+ ├── users_test.rb-14.html
55
+ ├── users_test.rb-18.html
56
+ ├── users_test.rb-21.html
57
+ ├── users_test.rb-25.html
58
+ ├── users_test.rb-26.html
59
+ ├── users_test.rb-29.html
60
+ ├── users_test.rb-32.html
61
+ ├── users_test.rb-36.html
62
+ ├── users_test.rb-37.html
63
+ └── users_test.rb-9.html
64
+
65
+ 4 directories, 17 files
66
+ ```
67
+
68
+ And each file content is just an HTML.
69
+ ```html
70
+ % cat tmp/html/rails52/test/system/users_test.rb-18.html
71
+ <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head>
72
+ <title>StillLifeTest</title>
73
+
74
+
75
+
76
+ <link rel="stylesheet" media="all" href="/assets/application-35729bfbaf9967f119234595ed222f7ab14859f304ab0acc5451afb387f637fa.css" data-turbolinks-track="reload" />
77
+ <script src="/assets/application-3c2e77f06bf9a01c87fc8ca44294f3d3879d89483d83b66a13a89fc07412dd59.js" data-turbolinks-track="reload"></script>
78
+ </head>
79
+
80
+ <body>
81
+ <p id="notice">User was successfully created.</p>
82
+
83
+ <p>
84
+ <strong>Name:</strong>
85
+ MyString
86
+ </p>
87
+
88
+ <a href="/users/980190963/edit">Edit</a> |
89
+ <a href="/users">Back</a>
90
+
91
+
92
+
93
+ </body></html>
94
+ ```
95
+
96
+
97
+ ## Usage Scenario
98
+
99
+ Consider you have a well-tested Rails 5.2 app, and you want to upgrade its Rails version to 6.0 without introducing any user-facing incompatibilities.
100
+ Then the workflow will be as follows:
101
+
102
+ ### 1. Draw a still_life with the 5.2 app
103
+
104
+ ```sh
105
+ % STILL_LIFE=rails52 rails test:system test
106
+ ```
107
+
108
+ ### 2. Do the upgrade job
109
+
110
+ ```sh
111
+ % bundle u
112
+ % rails app:update
113
+ ```
114
+ and push some more commits...
115
+
116
+ ### 3. Draw another still_life with the 6.0 app
117
+
118
+ ```sh
119
+ % STILL_LIFE=rails60 rails test:system test
120
+ ```
121
+
122
+ ### 4. Compare the results, and make sure there's no unexpected diffs
123
+
124
+ ```sh
125
+ % diff -r tmp/html/rails52 tmp/html/rails60
126
+ ```
127
+
128
+
129
+ ## Notes
130
+ ### Random Values
131
+
132
+ If your response includes some kind of random values, the test results may change between each test runs.
133
+ In such case, maybe you could specify a random seed, or mock the random source in your app, or `grep -v` is always your friend.
134
+
135
+
136
+ ## TODOs / Known Issues
137
+ - The Capybara monkeypatch sometimes fails to get the `page.body` due to Capybara timing problem
138
+ - Support older versions of Rails, Capybara, and Ruby
139
+
140
+
141
+ ## Contributing
142
+
143
+ Pull requests are welcome on GitHub at https://github.com/amatsuda/still_life.
144
+
145
+
146
+ ## Credit
147
+
148
+ The original idea of this library was implemented as a 10 LOC anonymous module by [@hotchpotch](https://github.com/hotchpotch) at Cookpad Inc. back in 2011 as written in [this slide](https://speakerdeck.com/a_matsuda/the-recipe-for-the-worlds-largest-rails-monolith?slide=129).
149
+
150
+ ## License
151
+
152
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList['test/**/*_test.rb'] - FileList['test/dummy_app/test/**/*_test.rb']
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "still_life"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'still_life/version'
4
+ require_relative 'still_life/action_dispatch_extension'
5
+ require_relative 'still_life/capybara_extension'
6
+ require_relative 'still_life/railtie'
7
+
8
+ module StillLife
9
+ def self.draw(html)
10
+ location = caller.detect {|c| c =~ /^#{Rails.root}/}.remove(Rails.root.to_s, /:in .*$/).prepend("#{ENV['STILL_LIFE'] || ENV['STILLLIFE']}/")
11
+
12
+ if html.present?
13
+ pathname, i = Rails.root.join("tmp/html/#{location.tr(':', '-')}.html"), 1
14
+ while pathname.exist?
15
+ i += 1
16
+ pathname = Rails.root.join("tmp/html/#{location.tr(':', '-')}_#{i}.html")
17
+ end
18
+ pathname.parent.mkpath
19
+ pathname.write html
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillLife
4
+ module ActionDispatchExtension
5
+ def get(*args)
6
+ super(*args).tap do
7
+ StillLife.draw(response.body)
8
+ end
9
+ end
10
+
11
+ def post(*args)
12
+ super(*args).tap do
13
+ StillLife.draw(response.body)
14
+ end
15
+ end
16
+
17
+ def put(*args)
18
+ super(*args).tap do
19
+ StillLife.draw(response.body)
20
+ end
21
+ end
22
+
23
+ def patch(*args)
24
+ super(*args).tap do
25
+ StillLife.draw(response.body)
26
+ end
27
+ end
28
+
29
+ def delete(*args)
30
+ super(*args).tap do
31
+ StillLife.draw(response.body)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillLife
4
+ module CapybaraExtension
5
+ module ElementExtension
6
+ def click(*)
7
+ return super if Thread.current[:_still_life_inside_modal]
8
+
9
+ body_was = session.body
10
+ super.tap do
11
+ session.find('body')
12
+ if session.body.present? && (session.body != body_was)
13
+ StillLife.draw(session.body)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ module SessionExtension
20
+ def visit(*)
21
+ super.tap do
22
+ if body.present?
23
+ StillLife.draw(body)
24
+ end
25
+ end
26
+ end
27
+
28
+ private def accept_modal(*)
29
+ Thread.current[:_still_life_inside_modal] = true
30
+ body_was = body
31
+ super.tap do
32
+ if body.present? && (body != body_was)
33
+ StillLife.draw(body)
34
+ end
35
+ end
36
+ ensure
37
+ Thread.current[:_still_life_inside_modal] = nil
38
+ end
39
+
40
+ private def dismiss_modal(*)
41
+ Thread.current[:_still_life_inside_modal] = true
42
+ body_was = body
43
+ super.tap do
44
+ if body.present? && (body != body_was)
45
+ StillLife.draw(body)
46
+ end
47
+ end
48
+ ensure
49
+ Thread.current[:_still_life_inside_modal] = nil
50
+ end
51
+ end
52
+
53
+ def self.included(*)
54
+ Capybara::Node::Element.prepend ElementExtension
55
+ Capybara::Session.prepend SessionExtension
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+
5
+ module StillLife
6
+ class Railtie < Rails::Railtie
7
+ initializer 'still_life' do
8
+ if ENV['STILL_LIFE'] || ENV['STILLLIFE']
9
+ ActiveSupport.on_load :action_dispatch_integration_test do
10
+ ActionDispatch::Integration::Session.prepend StillLife::ActionDispatchExtension
11
+ end
12
+ ActiveSupport.on_load :action_dispatch_system_test_case do
13
+ ActionDispatch::SystemTestCase.include StillLife::CapybaraExtension
14
+ end
15
+
16
+ begin
17
+ require 'rspec-rails'
18
+
19
+ #TODO maybe we could use some kind of hook instead of directly configuring here?
20
+ RSpec.configure do |config|
21
+ # config.prepend StillLife::ActionDispatchExtension, type: :request
22
+ config.prepend StillLife::ActionDispatchExtension, type: :controller
23
+ config.include StillLife::CapybaraExtension, type: :feature
24
+ end
25
+ rescue LoadError
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillLife
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,30 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "still_life/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "still_life"
8
+ spec.version = StillLife::VERSION
9
+ spec.authors = ["Akira Matsuda"]
10
+ spec.email = ["ronnie@dio.jp"]
11
+
12
+ spec.summary = 'Test result HTML recorder'
13
+ spec.description = 'Test result HTML recorder for capybara, test-unit, minitest, and RSpec'
14
+ spec.homepage = 'https://github.com/amatsuda/still_life'
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 2.0"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "byebug"
29
+ spec.add_development_dependency 'test-unit-rails'
30
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: still_life
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Akira Matsuda
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-03-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Test result HTML recorder for capybara, test-unit, minitest, and RSpec
70
+ email:
71
+ - ronnie@dio.jp
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - MIT-LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/setup
84
+ - lib/still_life.rb
85
+ - lib/still_life/action_dispatch_extension.rb
86
+ - lib/still_life/capybara_extension.rb
87
+ - lib/still_life/railtie.rb
88
+ - lib/still_life/version.rb
89
+ - still_life.gemspec
90
+ homepage: https://github.com/amatsuda/still_life
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubygems_version: 3.0.3
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Test result HTML recorder
113
+ test_files: []