jscov 0.1.0 → 0.5.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.
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