jscov 0.1.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f29b1e1e112c912b6a7213210d3f156c9ca57e2ec7e01a2c5c9c1770051d7156
4
- data.tar.gz: 5db3557ea568399e5de364d93fa6f7ba6beaad43f91b5e3dc2bf10d7beba48ef
3
+ metadata.gz: a5f6448f50d082e6d9ef6e8e74d8887a3f7e7c0cf1d3241134dbbca15030eb9b
4
+ data.tar.gz: dd84c1191c0a7bd3556e60244cc9f29b317b09d72eff52fab458309e6eb3cb46
5
5
  SHA512:
6
- metadata.gz: ca06dc98ada7976f0edaab3935cb89c1ea8808f0f40b16ba444350f7f97c6ddad2a5a2804d0c359924f00c411a46403bbf0a80ccf91608f2dacfde4a5d959f45
7
- data.tar.gz: 413eb1782c2cc65351c3b28fadb030d5fc79d4a3747dce8cb31596acc3f66ae68791a398d2774eaf0b099f02b2865ec8a0701bf14a6963eb78064ae31d67e8d6
6
+ metadata.gz: 21df5f4ab063f0010bf931947865e77336453f5d95de0e36d4a39efdec9ec479f2f38bcae69f9beba0f76959c8e5c672aeae2f50d02715e13e375aa420f43b6a
7
+ data.tar.gz: ea20b192571cce145769f71ce8634b5591e50f2f67ff864046cc65efa28ee963a90b162d47af513369322746b5b547845c84aefc8d66161289d067845910b181
data/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ [![Gem Version](https://badge.fury.io/rb/jscov.svg)](http://badge.fury.io/rb/jscov)
2
+ ![](https://github.com/kzkn/jscov/workflows/CI/badge.svg)
3
+
1
4
  # Jscov
2
5
 
3
6
  Collect JavaScript code coverages. It works with [istanbul](https://istanbul.js.org/).
@@ -41,29 +44,43 @@ module.exports = function (api) {
41
44
  }
42
45
  ```
43
46
 
44
- Mount `Jscov::Engine`. In route.rb:
47
+ Add Jscov middleware to rails middleware stack. Add the following in `config/environments/test.rb`:
45
48
 
46
49
  ```ruby
47
- if Jscov.enabled?
48
- mount Jscov::Engine => "/jscov"
50
+ [MyRailsApp]::Application.configure do |config|
51
+ ...
52
+ config.middleware.use Jscov::RackMiddleware
53
+ ...
49
54
  end
50
55
  ```
51
56
 
52
- Insert `jscov_script_tag` in your layout file:
57
+ Load rspec helper. In spec/rails_helper.rb:
53
58
 
54
- ```html
55
- <html>
56
- <head>
57
- <%= jscov_script_tag %>
58
- </head>
59
- ...
60
- </html>
59
+ ```ruby
60
+ require 'jscov/rspec'
61
61
  ```
62
62
 
63
- Load rspec helper. In spec/rails_helper.rb:
63
+ Configure selenium to capture the output of `console.log`:
64
64
 
65
65
  ```ruby
66
- require 'jscov/rspec'
66
+ RSpec.configure do |config|
67
+ config.before(type: :system) do
68
+ caps = Selenium::WebDriver::Remote::Capabilities.chrome(
69
+ 'goog:loggingPrefs' => { browser: 'ALL' }
70
+ )
71
+ driven_by :selenium, using: :headless_chrome, options: { capabilities: caps }
72
+ end
73
+ end
74
+ ```
75
+
76
+ And configure rspec to save coverage files after each examples:
77
+
78
+ ```ruby
79
+ RSpec.configure do |config|
80
+ config.after(type: :system) do
81
+ Jscov.save!
82
+ end
83
+ end
67
84
  ```
68
85
 
69
86
  Run `NODE_ENV=test RAILS_ENV=test bin/rails assets:precompile` for generating js codes that applied `istanbul`:
@@ -79,19 +96,66 @@ The collected coverages can be output as a report using [nyc](https://github.com
79
96
  $ npx nyc report --temp-dir=tmp/jscov
80
97
  ```
81
98
 
99
+ ### Tips
100
+
101
+ Selenium's `logs.get(:browser)` is a destructive method. `jscov` depends on it. If you use it out of `jscov`, it will affect to result of `jscov`, and vice versa.
102
+
103
+ You can pass browser logs to `Jscov.save!` to avoid this issue:
104
+
105
+ ```ruby
106
+ logs = Capybara.current_session.driver.browser.logs.get(:browser)
107
+ Jscov.save!(logs: logs)
108
+ ```
109
+
110
+ If you use multiple capybara sessions, you can pass your capybara sessions to `Jscov.save!` to save coverages that collected on your sessions:
111
+
112
+ ```
113
+ Jscov.save!(session: your_capybara_session)
114
+ ```
115
+
116
+ ### Vue.js
117
+
118
+ To collect coverage for `.vue` files, you will need to change the configurations.
119
+
120
+ In babel.config.js:
121
+
122
+ ```js
123
+ module.exports = function (api) {
124
+ // ....
125
+ return {
126
+ plugins: [
127
+ // ...
128
+ // Add below
129
+ isTestEnv && [
130
+ 'babel-plugin-istanbul',
131
+ {
132
+ extension: ['.js', '.vue']
133
+ }
134
+ ]
135
+ ].filter(Boolean)
136
+ }
137
+ }
138
+ ```
139
+
140
+ And, you need to add an argument to `nyc`:
141
+
142
+ ```bash
143
+ $ npx nyc report --temp-dir=tmp/jscov --extension=.vue
144
+ ```
145
+
82
146
  ## Configuration
83
147
 
84
- In `config/initializers/jscov.rb`, you can configure the following values.
148
+ You can configure the following values.
85
149
 
86
150
  ```ruby
87
151
  Jscov.configure do |config|
88
- # config.enabled = Rails.env.test?
89
152
  # config.coverage_report_dir_path = Rails.root.join("tmp/jscov")
90
153
  end
91
154
  ```
92
155
 
93
156
  ## Contributing
94
- Contribution directions go here.
157
+
158
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kzkn/jscov.
95
159
 
96
160
  ## License
97
161
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,10 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
- require "rake/testtask"
2
+ require "rspec/core/rake_task"
3
3
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.pattern = "test/**/*_test.rb"
7
- t.verbose = false
8
- end
4
+ RSpec::Core::RakeTask.new(:spec)
9
5
 
10
- task default: :test
6
+ task default: :spec
@@ -0,0 +1,70 @@
1
+ module Jscov
2
+ class Bless
3
+ def initialize(response)
4
+ @response = response
5
+ end
6
+
7
+ def result
8
+ [
9
+ @response[0],
10
+ headers,
11
+ blessed_body,
12
+ ]
13
+ end
14
+
15
+ private
16
+
17
+ def headers
18
+ @response[1]
19
+ end
20
+
21
+ def blessed_body
22
+ plain_body = @response[2]
23
+ return plain_body unless html?
24
+
25
+ blessed = []
26
+ plain_body.each do |fragment|
27
+ blessed << bless(fragment)
28
+ end
29
+
30
+ plain_body.close if plain_body.respond_to?(:close)
31
+
32
+ blessed
33
+ end
34
+
35
+ def html?
36
+ content_type = headers["Content-Type"]
37
+ content_type =~ /text\/html/
38
+ end
39
+
40
+ def bless(fragment)
41
+ index = fragment.index(/<head>/i)
42
+ if index
43
+ fragment.insert(index + "<head>".size, script_tag)
44
+ else
45
+ fragment
46
+ end
47
+ end
48
+
49
+ def script_tag
50
+ tag = "<script>#{self.class.js_code}</script>"
51
+ tag = tag.html_safe if tag.respond_to?(:html_safe)
52
+ tag
53
+ end
54
+
55
+ class << self
56
+ def js_code
57
+ <<~JS
58
+ window.addEventListener("unload", __jscov_dumpCoverage)
59
+
60
+ function __jscov_dumpCoverage() {
61
+ const cov = window.__coverage__
62
+ if (!cov) { return }
63
+
64
+ console.log('__jscov', JSON.stringify(cov))
65
+ }
66
+ JS
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,49 @@
1
+ require "json"
2
+ require "jscov/coverage"
3
+
4
+ module Jscov
5
+ class Collector
6
+ def initialize(session, logs)
7
+ @session = session || Capybara.current_session
8
+ @logs = logs
9
+ end
10
+
11
+ def coverages
12
+ return [] unless supported_driver?
13
+
14
+ dump_coverage
15
+
16
+ browser_logs
17
+ .map { |log| parse(log.message) }
18
+ .compact
19
+ .map { |cov| Coverage.new(cov) }
20
+ end
21
+
22
+ def supported_driver?
23
+ @session.driver.is_a?(Capybara::Selenium::Driver) &&
24
+ @session.driver.browser.respond_to?(:logs)
25
+ end
26
+
27
+ def dump_coverage
28
+ code = <<~JS
29
+ typeof __jscov_dumpCoverage === 'function' && __jscov_dumpCoverage()
30
+ JS
31
+ @session.execute_script(code)
32
+ end
33
+
34
+ def browser_logs
35
+ logs = @session.driver.browser.logs.get(:browser)
36
+ (@logs || []) + logs
37
+ end
38
+
39
+ def parse(message)
40
+ json_string = message[/__jscov" (.*)/, 1]
41
+ return if json_string.nil?
42
+
43
+ json = JSON.parse(json_string)
44
+ JSON.parse(json)
45
+ rescue JSON::ParserError
46
+ nil
47
+ end
48
+ end
49
+ end
@@ -1,16 +1,23 @@
1
1
  module Jscov
2
2
  class Configuration
3
- attr_accessor :enabled, :coverage_report_dir_path
3
+ attr_accessor :coverage_report_dir_path
4
4
 
5
5
  def initialize
6
6
  reset!
7
7
  end
8
8
 
9
- alias enabled? enabled
10
-
11
9
  def reset!
12
- self.enabled = Rails.env.test?
13
- self.coverage_report_dir_path = Rails.root.join("tmp/jscov")
10
+ self.coverage_report_dir_path = self.class.default_coverage_report_dir_path
11
+ end
12
+
13
+ class << self
14
+ def default_coverage_report_dir_path
15
+ if defined?(Rails)
16
+ Rails.root.join("tmp/jscov")
17
+ else
18
+ "tmp/jscov"
19
+ end
20
+ end
14
21
  end
15
22
  end
16
23
  end
@@ -1,3 +1,5 @@
1
+ require "time"
2
+
1
3
  module Jscov
2
4
  class Coverage
3
5
  def initialize(raw_data)
@@ -5,9 +7,16 @@ module Jscov
5
7
  end
6
8
 
7
9
  def save
8
- File.open(Jscov.new_coverage_file_path, "w") do |f|
10
+ File.open(self.class.new_coverage_file_path, "w") do |f|
9
11
  f.puts @raw_data.to_json
10
12
  end
11
13
  end
14
+
15
+ class << self
16
+ def new_coverage_file_path
17
+ name = "jscov_#{Time.now.to_i}_#{SecureRandom.uuid}.json"
18
+ Jscov.configuration.coverage_report_dir_path.join(name)
19
+ end
20
+ end
12
21
  end
13
22
  end
@@ -0,0 +1,16 @@
1
+ require "rack/request"
2
+ require "jscov/coverage"
3
+ require "jscov/bless"
4
+
5
+ module Jscov
6
+ class RackMiddleware
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ response = @app.call(env)
13
+ Bless.new(response).result
14
+ end
15
+ end
16
+ end
data/lib/jscov/rspec.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "capybara"
1
2
  require "jscov"
2
3
 
3
4
  RSpec.configure do |config|
data/lib/jscov/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jscov
2
- VERSION = '0.1.0'
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/jscov.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "securerandom"
2
- require "jscov/engine"
3
- require "jscov/railtie"
4
2
  require "jscov/configuration"
3
+ require "jscov/rack_middleware"
4
+ require "jscov/collector"
5
5
 
6
6
  module Jscov
7
7
  class << self
@@ -13,16 +13,15 @@ module Jscov
13
13
  yield configuration
14
14
  end
15
15
 
16
- delegate :enabled?, to: :configuration
17
-
18
- def new_coverage_file_path
19
- configuration.coverage_report_dir_path.join("jscov_#{Time.current.to_i}_#{SecureRandom.uuid}.json")
20
- end
21
-
22
16
  def clean!
23
17
  dir_path = configuration.coverage_report_dir_path
24
18
  FileUtils.rm_f(Dir.glob(dir_path.join("jscov_*.json")))
25
19
  FileUtils.mkdir_p(dir_path)
26
20
  end
21
+
22
+ def save!(session: nil, logs: nil)
23
+ collector = Collector.new(session, logs)
24
+ collector.coverages.each(&:save)
25
+ end
27
26
  end
28
27
  end
metadata CHANGED
@@ -1,45 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jscov
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuki Nishikawa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-27 00:00:00.000000000 Z
11
+ date: 2021-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: railties
14
+ name: rack
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.2'
19
+ version: '2.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5.2'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: actionview
28
+ name: capybara
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '5.2'
33
+ version: '3.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '5.2'
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: selenium-webdriver
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '4.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '4.0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: sqlite3
56
+ name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
@@ -52,7 +66,41 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
- description: Jscov is a Rails Engine for collecting JavaScript code coverages.
69
+ - !ruby/object:Gem::Dependency
70
+ name: webdrivers
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '5.2'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '7.0'
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '5.2'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '7.0'
103
+ description: A rack middleware for collecting JavaScript code coverages.
56
104
  email:
57
105
  - kzkn@users.noreply.github.com
58
106
  executables: []
@@ -62,18 +110,14 @@ files:
62
110
  - MIT-LICENSE
63
111
  - README.md
64
112
  - Rakefile
65
- - app/controllers/jscov/application_controller.rb
66
- - app/controllers/jscov/coverages_controller.rb
67
- - config/routes.rb
68
113
  - lib/jscov.rb
114
+ - lib/jscov/bless.rb
115
+ - lib/jscov/collector.rb
69
116
  - lib/jscov/configuration.rb
70
117
  - lib/jscov/coverage.rb
71
- - lib/jscov/engine.rb
72
- - lib/jscov/helper.rb
73
- - lib/jscov/railtie.rb
118
+ - lib/jscov/rack_middleware.rb
74
119
  - lib/jscov/rspec.rb
75
120
  - lib/jscov/version.rb
76
- - lib/tasks/jscov_tasks.rake
77
121
  homepage: https://github.com/kzkn/jscov
78
122
  licenses:
79
123
  - MIT
@@ -93,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
137
  - !ruby/object:Gem::Version
94
138
  version: '0'
95
139
  requirements: []
96
- rubygems_version: 3.0.3
140
+ rubygems_version: 3.2.3
97
141
  signing_key:
98
142
  specification_version: 4
99
143
  summary: Collect JavaScript code coverages.
@@ -1,5 +0,0 @@
1
- module Jscov
2
- class ApplicationController < ActionController::Base
3
- protect_from_forgery with: :exception
4
- end
5
- end
@@ -1,14 +0,0 @@
1
- require "jscov/coverage"
2
-
3
- module Jscov
4
- class CoveragesController < ApplicationController
5
- skip_before_action :verify_authenticity_token
6
-
7
- def create
8
- raw_data = JSON.parse(params[:coverage])
9
- coverage = Jscov::Coverage.new(raw_data)
10
- coverage.save
11
- head :no_content
12
- end
13
- end
14
- end
data/config/routes.rb DELETED
@@ -1,3 +0,0 @@
1
- Jscov::Engine.routes.draw do
2
- resources :coverages, only: %i[create]
3
- end
data/lib/jscov/engine.rb DELETED
@@ -1,5 +0,0 @@
1
- module Jscov
2
- class Engine < ::Rails::Engine
3
- isolate_namespace Jscov
4
- end
5
- end
data/lib/jscov/helper.rb DELETED
@@ -1,26 +0,0 @@
1
- module Jscov
2
- module Helper
3
- def jscov_script_tag
4
- return unless Jscov.enabled?
5
-
6
- javascript_tag jscov_javascript_code
7
- end
8
-
9
- def jscov_javascript_code
10
- <<~JS
11
- (function () {
12
- window.addEventListener('unload', uploadCoverage)
13
-
14
- function uploadCoverage () {
15
- const cov = window.__coverage__
16
- if (!cov) { return }
17
-
18
- const data = new FormData()
19
- data.append('coverage', JSON.stringify(cov))
20
- navigator.sendBeacon('#{jscov.coverages_path}', data)
21
- }
22
- })()
23
- JS
24
- end
25
- end
26
- end
data/lib/jscov/railtie.rb DELETED
@@ -1,11 +0,0 @@
1
- require "jscov/helper"
2
-
3
- module Jscov
4
- class Railtie < ::Rails::Railtie
5
- initializer "jscov" do
6
- ActiveSupport.on_load(:action_view) do
7
- include Helper
8
- end
9
- end
10
- end
11
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :jscov do
3
- # # Task goes here
4
- # end