puppeteer-ruby 0.31.6 → 0.32.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 +4 -4
- data/CHANGELOG.md +13 -2
- data/docs/api_coverage.md +10 -10
- data/lib/puppeteer.rb +5 -0
- data/lib/puppeteer/browser.rb +18 -1
- data/lib/puppeteer/coverage.rb +106 -0
- data/lib/puppeteer/css_coverage.rb +110 -0
- data/lib/puppeteer/js_coverage.rb +119 -0
- data/lib/puppeteer/page.rb +4 -33
- data/lib/puppeteer/protocol_stream_reader.rb +45 -0
- data/lib/puppeteer/tracing.rb +50 -0
- data/lib/puppeteer/version.rb +1 -1
- data/puppeteer-ruby.gemspec +1 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ac3b08e88fc4d18d756dd760d8960783498e4fb84c23b02e248471de8097636
|
4
|
+
data.tar.gz: b9c14719e9b13c56eb529cc58b71648985245158b4c3edffa9a6cff4f191463a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 103c5d80b62d51bfa877884627ef9544a05cc901abaa8386f877e9eb64da58438e540a6e263dc47f176aca2709d0e4f43a2a43bc7922226cbd428b04a3f23d6d
|
7
|
+
data.tar.gz: a7adb58a5747c563c8f55041e007516a497c7721d25ec69a3ec20a0e75ce9016362aa0f1f468e4c78a6cbbece37964e71d90c7bc5b8c1b7aa0cfdd324845de27
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,19 @@
|
|
1
|
-
### master [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.
|
1
|
+
### master [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.32.0...master)]
|
2
2
|
|
3
3
|
* xxx
|
4
4
|
|
5
|
-
###
|
5
|
+
### 0.32.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.31.6...0.32.0)]
|
6
|
+
|
7
|
+
New features:
|
8
|
+
|
9
|
+
* Tracing
|
10
|
+
* JS/CSS coverages
|
11
|
+
|
12
|
+
Improvement:
|
13
|
+
|
14
|
+
* Increase stability [#92](https://github.com/YusukeIwaki/puppeteer-ruby/pull/92)
|
15
|
+
|
16
|
+
### 0.31.6 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.31.5...0.31.6)]
|
6
17
|
|
7
18
|
Improvement:
|
8
19
|
|
data/docs/api_coverage.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# API coverages
|
2
2
|
- Puppeteer version: v8.0.0
|
3
|
-
- puppeteer-ruby version: 0.
|
3
|
+
- puppeteer-ruby version: 0.32.0
|
4
4
|
|
5
5
|
## Puppeteer
|
6
6
|
|
@@ -124,7 +124,7 @@
|
|
124
124
|
* target
|
125
125
|
* title
|
126
126
|
* ~~touchscreen~~
|
127
|
-
*
|
127
|
+
* tracing
|
128
128
|
* type => `#type_text`
|
129
129
|
* url
|
130
130
|
* viewport
|
@@ -170,10 +170,10 @@
|
|
170
170
|
|
171
171
|
* ~~tap~~
|
172
172
|
|
173
|
-
##
|
173
|
+
## Tracing
|
174
174
|
|
175
|
-
*
|
176
|
-
*
|
175
|
+
* start
|
176
|
+
* stop
|
177
177
|
|
178
178
|
## FileChooser
|
179
179
|
|
@@ -337,12 +337,12 @@
|
|
337
337
|
* detach
|
338
338
|
* send
|
339
339
|
|
340
|
-
##
|
340
|
+
## Coverage
|
341
341
|
|
342
|
-
*
|
343
|
-
*
|
344
|
-
*
|
345
|
-
*
|
342
|
+
* startCSSCoverage => `#start_css_coverage`
|
343
|
+
* startJSCoverage => `#start_js_coverage`
|
344
|
+
* stopCSSCoverage => `#stop_css_coverage`
|
345
|
+
* stopJSCoverage => `#stop_js_coverage`
|
346
346
|
|
347
347
|
## TimeoutError
|
348
348
|
|
data/lib/puppeteer.rb
CHANGED
@@ -26,6 +26,8 @@ require 'puppeteer/browser_runner'
|
|
26
26
|
require 'puppeteer/cdp_session'
|
27
27
|
require 'puppeteer/connection'
|
28
28
|
require 'puppeteer/console_message'
|
29
|
+
require 'puppeteer/coverage'
|
30
|
+
require 'puppeteer/css_coverage'
|
29
31
|
require 'puppeteer/custom_query_handler'
|
30
32
|
require 'puppeteer/devices'
|
31
33
|
require 'puppeteer/dialog'
|
@@ -36,6 +38,7 @@ require 'puppeteer/execution_context'
|
|
36
38
|
require 'puppeteer/file_chooser'
|
37
39
|
require 'puppeteer/frame'
|
38
40
|
require 'puppeteer/frame_manager'
|
41
|
+
require 'puppeteer/js_coverage'
|
39
42
|
require 'puppeteer/js_handle'
|
40
43
|
require 'puppeteer/keyboard'
|
41
44
|
require 'puppeteer/launcher'
|
@@ -43,12 +46,14 @@ require 'puppeteer/lifecycle_watcher'
|
|
43
46
|
require 'puppeteer/mouse'
|
44
47
|
require 'puppeteer/network_manager'
|
45
48
|
require 'puppeteer/page'
|
49
|
+
require 'puppeteer/protocol_stream_reader'
|
46
50
|
require 'puppeteer/puppeteer'
|
47
51
|
require 'puppeteer/query_handler_manager'
|
48
52
|
require 'puppeteer/remote_object'
|
49
53
|
require 'puppeteer/request'
|
50
54
|
require 'puppeteer/response'
|
51
55
|
require 'puppeteer/target'
|
56
|
+
require 'puppeteer/tracing'
|
52
57
|
require 'puppeteer/timeout_settings'
|
53
58
|
require 'puppeteer/touch_screen'
|
54
59
|
require 'puppeteer/version'
|
data/lib/puppeteer/browser.rb
CHANGED
@@ -45,6 +45,7 @@ class Puppeteer::Browser
|
|
45
45
|
@contexts[context_id] = Puppeteer::BrowserContext.new(@connection, self, context_id)
|
46
46
|
end
|
47
47
|
@targets = {}
|
48
|
+
@wait_for_creating_targets = {}
|
48
49
|
@connection.on_event(ConnectionEmittedEvents::Disconnected) do
|
49
50
|
emit_event(BrowserEmittedEvents::Disconnected)
|
50
51
|
end
|
@@ -125,8 +126,10 @@ class Puppeteer::Browser
|
|
125
126
|
ignore_https_errors: @ignore_https_errors,
|
126
127
|
default_viewport: @default_viewport,
|
127
128
|
)
|
128
|
-
# assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
|
129
129
|
@targets[target_info.target_id] = target
|
130
|
+
if_present(@wait_for_creating_targets.delete(target_info.target_id)) do |promise|
|
131
|
+
promise.fulfill(target)
|
132
|
+
end
|
130
133
|
if await target.initialized_promise
|
131
134
|
emit_event(BrowserEmittedEvents::TargetCreated, target)
|
132
135
|
context.emit_event(BrowserContextEmittedEvents::TargetCreated, target)
|
@@ -139,6 +142,9 @@ class Puppeteer::Browser
|
|
139
142
|
target = @targets[target_id]
|
140
143
|
target.ignore_initialize_callback_promise
|
141
144
|
@targets.delete(target_id)
|
145
|
+
if_present(@wait_for_creating_targets.delete(target_id)) do |promise|
|
146
|
+
promise.reject('target destroyed')
|
147
|
+
end
|
142
148
|
target.closed_callback
|
143
149
|
if await target.initialized_promise
|
144
150
|
emit_event(BrowserEmittedEvents::TargetDestroyed, target)
|
@@ -184,6 +190,17 @@ class Puppeteer::Browser
|
|
184
190
|
result = @connection.send_message('Target.createTarget', **create_target_params)
|
185
191
|
target_id = result['targetId']
|
186
192
|
target = @targets[target_id]
|
193
|
+
unless target
|
194
|
+
# Target.targetCreated is often notified before the response of Target.createdTarget.
|
195
|
+
# https://github.com/YusukeIwaki/puppeteer-ruby/issues/91
|
196
|
+
# D, [2021-04-07T03:00:10.125241 #187] DEBUG -- : SEND >> {"method":"Target.createTarget","params":{"url":"about:blank","browserContextId":"56A86FC3391B50180CF9A6450A0D8C21"},"id":3}
|
197
|
+
# D, [2021-04-07T03:00:10.142396 #187] DEBUG -- : RECV << {"id"=>3, "result"=>{"targetId"=>"A518447C415A1A3E1A8979454A155632"}}
|
198
|
+
# D, [2021-04-07T03:00:10.145360 #187] DEBUG -- : RECV << {"method"=>"Target.targetCreated", "params"=>{"targetInfo"=>{"targetId"=>"A518447C415A1A3E1A8979454A155632", "type"=>"page", "title"=>"", "url"=>"", "attached"=>false, "canAccessOpener"=>false, "browserContextId"=>"56A86FC3391B50180CF9A6450A0D8C21"}}}
|
199
|
+
# This is just a workaround logic...
|
200
|
+
Rollbar.info("Workaround of YusukeIwaki/puppeteer-ruby#91")
|
201
|
+
@wait_for_creating_targets[target_id] = resolvable_future
|
202
|
+
target = await @wait_for_creating_targets[target_id]
|
203
|
+
end
|
187
204
|
await target.initialized_promise
|
188
205
|
await target.page
|
189
206
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
class Puppeteer::Coverage
|
2
|
+
# @param client [Puppeteer::CDPSession]
|
3
|
+
def initialize(client)
|
4
|
+
@js = Puppeteer::JSCoverage.new(client)
|
5
|
+
@css = Puppeteer::CSSCoverage.new(client)
|
6
|
+
end
|
7
|
+
|
8
|
+
def start_js_coverage(reset_on_navigation: nil, report_anonymous_scripts: nil)
|
9
|
+
@js.start(
|
10
|
+
reset_on_navigation: reset_on_navigation,
|
11
|
+
report_anonymous_scripts: report_anonymous_scripts,
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop_js_coverage
|
16
|
+
@js.stop
|
17
|
+
end
|
18
|
+
|
19
|
+
def js_coverage(reset_on_navigation: nil, report_anonymous_scripts: nil, &block)
|
20
|
+
unless block
|
21
|
+
raise ArgumentError.new('Block must be given')
|
22
|
+
end
|
23
|
+
|
24
|
+
start_js_coverage(
|
25
|
+
reset_on_navigation: reset_on_navigation,
|
26
|
+
report_anonymous_scripts: report_anonymous_scripts,
|
27
|
+
)
|
28
|
+
block.call
|
29
|
+
stop_js_coverage
|
30
|
+
end
|
31
|
+
|
32
|
+
def start_css_coverage(reset_on_navigation: nil)
|
33
|
+
@css.start(reset_on_navigation: reset_on_navigation)
|
34
|
+
end
|
35
|
+
|
36
|
+
def stop_css_coverage
|
37
|
+
@css.stop
|
38
|
+
end
|
39
|
+
|
40
|
+
def css_coverage(reset_on_navigation: nil, &block)
|
41
|
+
unless block
|
42
|
+
raise ArgumentError.new('Block must be given')
|
43
|
+
end
|
44
|
+
|
45
|
+
start_css_coverage(reset_on_navigation: reset_on_navigation)
|
46
|
+
block.call
|
47
|
+
stop_css_coverage
|
48
|
+
end
|
49
|
+
|
50
|
+
module UtilFunctions
|
51
|
+
private def convert_to_disjoint_ranges(nested_ranges)
|
52
|
+
points = []
|
53
|
+
nested_ranges.each do |range|
|
54
|
+
points << { offset: range['startOffset'], type: 0, range: range }
|
55
|
+
points << { offset: range['endOffset'], type: 1, range: range }
|
56
|
+
end
|
57
|
+
|
58
|
+
# Sort points to form a valid parenthesis sequence.
|
59
|
+
points.sort! do |a, b|
|
60
|
+
if a[:offset] != b[:offset]
|
61
|
+
# Sort with increasing offsets.
|
62
|
+
a[:offset] <=> b[:offset]
|
63
|
+
elsif a[:type] != b[:type]
|
64
|
+
# All "end" points should go before "start" points.
|
65
|
+
b[:type] <=> a[:type]
|
66
|
+
else
|
67
|
+
alength = a[:range]['endOffset'] - a[:range]['startOffset']
|
68
|
+
blength = b[:range]['endOffset'] - b[:range]['startOffset']
|
69
|
+
if a[:type] == 0
|
70
|
+
# For two "start" points, the one with longer range goes first.
|
71
|
+
blength <=> alength
|
72
|
+
else
|
73
|
+
# For two "end" points, the one with shorter range goes first.
|
74
|
+
alength <=> blength
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
hit_count_stack = []
|
80
|
+
results = []
|
81
|
+
last_offset = 0
|
82
|
+
# Run scanning line to intersect all ranges.
|
83
|
+
points.each do |point|
|
84
|
+
if !hit_count_stack.empty? && last_offset < point[:offset] && hit_count_stack.last > 0
|
85
|
+
last_result = results.last
|
86
|
+
if last_result && last_result[:end] == last_offset
|
87
|
+
last_result[:end] = point[:offset]
|
88
|
+
else
|
89
|
+
results << { start: last_offset, end: point[:offset] }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
last_offset = point[:offset]
|
93
|
+
if point[:type] == 0
|
94
|
+
hit_count_stack << point[:range]['count']
|
95
|
+
else
|
96
|
+
hit_count_stack.pop
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Filter out empty ranges.
|
101
|
+
results.select do |range|
|
102
|
+
range[:end] - range[:start] > 1
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require_relative './coverage'
|
2
|
+
|
3
|
+
class Puppeteer::CSSCoverage
|
4
|
+
include Puppeteer::Coverage::UtilFunctions
|
5
|
+
|
6
|
+
class Item
|
7
|
+
def initialize(url:, ranges:, text:)
|
8
|
+
@url = url
|
9
|
+
@ranges = ranges
|
10
|
+
@text = text
|
11
|
+
end
|
12
|
+
attr_reader :url, :ranges, :text
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param client [Puppeteer::CDPSession]
|
16
|
+
def initialize(client)
|
17
|
+
@client = client
|
18
|
+
@enabled = false
|
19
|
+
@stylesheet_urls = {}
|
20
|
+
@stylesheet_sources = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def start(reset_on_navigation: nil)
|
24
|
+
raise 'CSSCoverage is already enabled' if @enabled
|
25
|
+
|
26
|
+
@reset_on_navigation =
|
27
|
+
if [true, false].include?(reset_on_navigation)
|
28
|
+
reset_on_navigation
|
29
|
+
else
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
@enabled = true
|
34
|
+
@stylesheet_urls.clear
|
35
|
+
@stylesheet_sources.clear
|
36
|
+
@event_listeners = []
|
37
|
+
@event_listeners << @client.add_event_listener('CSS.styleSheetAdded') do |event|
|
38
|
+
future { on_stylesheet(event) }
|
39
|
+
end
|
40
|
+
@event_listeners << @client.add_event_listener('Runtime.executionContextsCleared') do
|
41
|
+
on_execution_contexts_cleared
|
42
|
+
end
|
43
|
+
await_all(
|
44
|
+
@client.async_send_message('DOM.enable'),
|
45
|
+
@client.async_send_message('CSS.enable'),
|
46
|
+
@client.async_send_message('CSS.startRuleUsageTracking'),
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
private def on_execution_contexts_cleared
|
51
|
+
return unless @reset_on_navigation
|
52
|
+
@stylesheet_urls.clear
|
53
|
+
@stylesheet_sources.clear
|
54
|
+
end
|
55
|
+
|
56
|
+
private def on_stylesheet(event)
|
57
|
+
header = event['header']
|
58
|
+
source_url =
|
59
|
+
if header['sourceURL'] == ""
|
60
|
+
nil
|
61
|
+
else
|
62
|
+
header['sourceURL']
|
63
|
+
end
|
64
|
+
|
65
|
+
# Ignore anonymous scripts
|
66
|
+
return if !source_url
|
67
|
+
|
68
|
+
response = @client.send_message('CSS.getStyleSheetText', styleSheetId: header['styleSheetId'])
|
69
|
+
@stylesheet_urls[header['styleSheetId']] = source_url
|
70
|
+
@stylesheet_sources[header['styleSheetId']] = response['text']
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def stop
|
75
|
+
raise 'CSSCoverage is not enabled' unless @enabled
|
76
|
+
@enabled = false
|
77
|
+
|
78
|
+
rule_tracking_response = @client.send_message('CSS.stopRuleUsageTracking')
|
79
|
+
await_all(
|
80
|
+
@client.async_send_message('CSS.disable'),
|
81
|
+
@client.async_send_message('DOM.disable'),
|
82
|
+
)
|
83
|
+
@client.remove_event_listener(*@event_listeners)
|
84
|
+
|
85
|
+
# aggregate by styleSheetId
|
86
|
+
stylesheet_id_to_coverage = {}
|
87
|
+
rule_tracking_response['ruleUsage'].each do |entry|
|
88
|
+
ranges = stylesheet_id_to_coverage[entry['styleSheetId']]
|
89
|
+
unless ranges
|
90
|
+
ranges = []
|
91
|
+
stylesheet_id_to_coverage[entry['styleSheetId']] = ranges
|
92
|
+
end
|
93
|
+
|
94
|
+
ranges << {
|
95
|
+
'startOffset' => entry['startOffset'],
|
96
|
+
'endOffset' => entry['endOffset'],
|
97
|
+
'count' => entry['used'] ? 1 : 0,
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
coverage = []
|
102
|
+
@stylesheet_urls.each do |stylesheet_id, url|
|
103
|
+
text = @stylesheet_sources[stylesheet_id]
|
104
|
+
ranges = convert_to_disjoint_ranges(stylesheet_id_to_coverage[stylesheet_id] || [])
|
105
|
+
coverage << Item.new(url: url, ranges: ranges, text: text)
|
106
|
+
end
|
107
|
+
|
108
|
+
coverage
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require_relative './coverage'
|
2
|
+
|
3
|
+
class Puppeteer::JSCoverage
|
4
|
+
include Puppeteer::Coverage::UtilFunctions
|
5
|
+
|
6
|
+
class Item
|
7
|
+
def initialize(url:, ranges:, text:)
|
8
|
+
@url = url
|
9
|
+
@ranges = ranges
|
10
|
+
@text = text
|
11
|
+
end
|
12
|
+
attr_reader :url, :ranges, :text
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param client [Puppeteer::CDPSession]
|
16
|
+
def initialize(client)
|
17
|
+
@client = client
|
18
|
+
@enabled = false
|
19
|
+
@script_urls = {}
|
20
|
+
@script_sources = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def start(reset_on_navigation: nil, report_anonymous_scripts: nil)
|
24
|
+
raise 'JSCoverage is already enabled' if @enabled
|
25
|
+
|
26
|
+
@reset_on_navigation =
|
27
|
+
if [true, false].include?(reset_on_navigation)
|
28
|
+
reset_on_navigation
|
29
|
+
else
|
30
|
+
true
|
31
|
+
end
|
32
|
+
@report_anonymous_scripts = report_anonymous_scripts || false
|
33
|
+
@enabled = true
|
34
|
+
@script_urls.clear
|
35
|
+
@script_sources.clear
|
36
|
+
@event_listeners = []
|
37
|
+
@event_listeners << @client.add_event_listener('Debugger.scriptParsed') do |event|
|
38
|
+
future { on_script_parsed(event) }
|
39
|
+
end
|
40
|
+
@event_listeners << @client.add_event_listener('Runtime.executionContextsCleared') do
|
41
|
+
on_execution_contexts_cleared
|
42
|
+
end
|
43
|
+
await_all(
|
44
|
+
@client.async_send_message('Profiler.enable'),
|
45
|
+
@client.async_send_message('Profiler.startPreciseCoverage',
|
46
|
+
callCount: false,
|
47
|
+
detailed: true,
|
48
|
+
),
|
49
|
+
@client.async_send_message('Debugger.enable'),
|
50
|
+
@client.async_send_message('Debugger.setSkipAllPauses', skip: true),
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
private def on_execution_contexts_cleared
|
55
|
+
return unless @reset_on_navigation
|
56
|
+
@script_urls.clear
|
57
|
+
@script_sources.clear
|
58
|
+
end
|
59
|
+
|
60
|
+
private def on_script_parsed(event)
|
61
|
+
url =
|
62
|
+
if event['url'] == ""
|
63
|
+
nil
|
64
|
+
else
|
65
|
+
event['url']
|
66
|
+
end
|
67
|
+
|
68
|
+
# Ignore puppeteer-injected scripts
|
69
|
+
return if url == Puppeteer::ExecutionContext::EVALUATION_SCRIPT_URL
|
70
|
+
|
71
|
+
# Ignore other anonymous scripts unless the reportAnonymousScripts option is true.
|
72
|
+
return if !url && !@report_anonymous_scripts
|
73
|
+
|
74
|
+
response = @client.send_message('Debugger.getScriptSource', scriptId: event['scriptId'])
|
75
|
+
@script_urls[event['scriptId']] = url
|
76
|
+
@script_sources[event['scriptId']] = response['scriptSource']
|
77
|
+
end
|
78
|
+
|
79
|
+
def stop
|
80
|
+
raise 'JSCoverage is not enabled' unless @enabled
|
81
|
+
@enabled = false
|
82
|
+
|
83
|
+
results = await_all(
|
84
|
+
@client.async_send_message('Profiler.takePreciseCoverage'),
|
85
|
+
@client.async_send_message('Profiler.stopPreciseCoverage'),
|
86
|
+
@client.async_send_message('Profiler.disable'),
|
87
|
+
@client.async_send_message('Debugger.disable'),
|
88
|
+
)
|
89
|
+
@client.remove_event_listener(*@event_listeners)
|
90
|
+
|
91
|
+
coverage = []
|
92
|
+
profile_response = results.first
|
93
|
+
profile_response['result'].each do |entry|
|
94
|
+
url = @script_urls[entry['scriptId']]
|
95
|
+
|
96
|
+
if @report_anonymous_scripts
|
97
|
+
url ||= "debugger://VM#{entry['scriptId']}"
|
98
|
+
end
|
99
|
+
|
100
|
+
text = @script_sources[entry['scriptId']]
|
101
|
+
next if !text || !url
|
102
|
+
|
103
|
+
flatten_ranges = []
|
104
|
+
entry['functions'].each do |func|
|
105
|
+
func['ranges'].each do |range|
|
106
|
+
flatten_ranges << range
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
coverage << Item.new(
|
111
|
+
url: url,
|
112
|
+
ranges: convert_to_disjoint_ranges(flatten_ranges),
|
113
|
+
text: text,
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
coverage
|
118
|
+
end
|
119
|
+
end
|
data/lib/puppeteer/page.rb
CHANGED
@@ -39,9 +39,9 @@ class Puppeteer::Page
|
|
39
39
|
# @accessibility = Accessibility.new(client)
|
40
40
|
@frame_manager = Puppeteer::FrameManager.new(client, self, ignore_https_errors, @timeout_settings)
|
41
41
|
@emulation_manager = Puppeteer::EmulationManager.new(client)
|
42
|
-
|
42
|
+
@tracing = Puppeteer::Tracing.new(client)
|
43
43
|
@page_bindings = {}
|
44
|
-
|
44
|
+
@coverage = Puppeteer::Coverage.new(client)
|
45
45
|
@javascript_enabled = true
|
46
46
|
@screenshot_task_queue = ScreenshotTaskQueue.new
|
47
47
|
|
@@ -245,7 +245,7 @@ class Puppeteer::Page
|
|
245
245
|
@frame_manager.main_frame
|
246
246
|
end
|
247
247
|
|
248
|
-
attr_reader :touch_screen, :coverage, :accessibility
|
248
|
+
attr_reader :touch_screen, :coverage, :tracing, :accessibility
|
249
249
|
|
250
250
|
def keyboard(&block)
|
251
251
|
@keyboard.instance_eval(&block) unless block.nil?
|
@@ -986,35 +986,6 @@ class Puppeteer::Page
|
|
986
986
|
buffer
|
987
987
|
end
|
988
988
|
|
989
|
-
class ProtocolStreamReader
|
990
|
-
def initialize(client:, handle:, path:)
|
991
|
-
@client = client
|
992
|
-
@handle = handle
|
993
|
-
@path = path
|
994
|
-
end
|
995
|
-
|
996
|
-
def read
|
997
|
-
out = StringIO.new
|
998
|
-
File.open(@path, 'wb') do |file|
|
999
|
-
eof = false
|
1000
|
-
until eof
|
1001
|
-
response = @client.send_message('IO.read', handle: @handle)
|
1002
|
-
eof = response['eof']
|
1003
|
-
data =
|
1004
|
-
if response['base64Encoded']
|
1005
|
-
Base64.decode64(response['data'])
|
1006
|
-
else
|
1007
|
-
response['data']
|
1008
|
-
end
|
1009
|
-
out.write(data)
|
1010
|
-
file.write(data)
|
1011
|
-
end
|
1012
|
-
end
|
1013
|
-
@client.send_message('IO.close', handle: @handle)
|
1014
|
-
out.read
|
1015
|
-
end
|
1016
|
-
end
|
1017
|
-
|
1018
989
|
class PrintToPdfIsNotImplementedError < StandardError
|
1019
990
|
def initialize
|
1020
991
|
super('pdf() is only available in headless mode. See https://github.com/puppeteer/puppeteer/issues/1829')
|
@@ -1028,7 +999,7 @@ class Puppeteer::Page
|
|
1028
999
|
set_transparent_background_color if omit_background
|
1029
1000
|
result = @client.send_message('Page.printToPDF', pdf_options.page_print_args)
|
1030
1001
|
reset_default_background_color if omit_background
|
1031
|
-
ProtocolStreamReader.new(client: @client, handle: result['stream'], path: pdf_options.path).read
|
1002
|
+
Puppeteer::ProtocolStreamReader.new(client: @client, handle: result['stream'], path: pdf_options.path).read
|
1032
1003
|
rescue => err
|
1033
1004
|
if err.message.include?('PrintToPDF is not implemented')
|
1034
1005
|
raise PrintToPdfIsNotImplementedError.new
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Puppeteer::ProtocolStreamReader
|
2
|
+
def initialize(client:, handle:, path:)
|
3
|
+
@client = client
|
4
|
+
@handle = handle
|
5
|
+
@path = path
|
6
|
+
end
|
7
|
+
|
8
|
+
def read
|
9
|
+
StringIO.open do |out|
|
10
|
+
if @path
|
11
|
+
File.open(@path, 'wb') do |file|
|
12
|
+
io_read do |data|
|
13
|
+
out.write(data)
|
14
|
+
file.write(data)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
io_read { |data| out.write(data) }
|
19
|
+
end
|
20
|
+
io_close
|
21
|
+
|
22
|
+
out.string
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private def io_read(&block)
|
27
|
+
eof = false
|
28
|
+
until eof
|
29
|
+
response = @client.send_message('IO.read', handle: @handle)
|
30
|
+
eof = response['eof']
|
31
|
+
data =
|
32
|
+
if response['base64Encoded']
|
33
|
+
Base64.decode64(response['data'])
|
34
|
+
else
|
35
|
+
response['data']
|
36
|
+
end
|
37
|
+
block.call(data)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
private def io_close
|
43
|
+
@client.send_message('IO.close', handle: @handle)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Puppeteer::Tracing
|
2
|
+
# @param client [Puppeteer::CDPSession]
|
3
|
+
def initialize(client)
|
4
|
+
@client = client
|
5
|
+
@recording = false
|
6
|
+
end
|
7
|
+
|
8
|
+
DEFAULT_CATEGORIES = [
|
9
|
+
'-*',
|
10
|
+
'devtools.timeline',
|
11
|
+
'v8.execute',
|
12
|
+
'disabled-by-default-devtools.timeline',
|
13
|
+
'disabled-by-default-devtools.timeline.frame',
|
14
|
+
'toplevel',
|
15
|
+
'blink.console',
|
16
|
+
'blink.user_timing',
|
17
|
+
'latencyInfo',
|
18
|
+
'disabled-by-default-devtools.timeline.stack',
|
19
|
+
'disabled-by-default-v8.cpu_profiler',
|
20
|
+
'disabled-by-default-v8.cpu_profiler.hires',
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
def start(path: nil, screenshots: nil, categories: nil)
|
24
|
+
option_categories = categories || DEFAULT_CATEGORIES.dup
|
25
|
+
|
26
|
+
if screenshots
|
27
|
+
option_categories << 'disabled-by-default-devtools.screenshot'
|
28
|
+
end
|
29
|
+
|
30
|
+
@path = path
|
31
|
+
@recording = true
|
32
|
+
@client.send_message('Tracing.start',
|
33
|
+
transferMode: 'ReturnAsStream',
|
34
|
+
categories: option_categories.join(','),
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
stream_promise = resolvable_future do |f|
|
40
|
+
@client.once('Tracing.tracingComplete') do |event|
|
41
|
+
f.fulfill(event['stream'])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@client.send_message('Tracing.end')
|
45
|
+
@recording = false
|
46
|
+
|
47
|
+
stream = await stream_promise
|
48
|
+
Puppeteer::ProtocolStreamReader.new(client: @client, handle: stream, path: @path).read
|
49
|
+
end
|
50
|
+
end
|
data/lib/puppeteer/version.rb
CHANGED
data/puppeteer-ruby.gemspec
CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency 'dry-inflector'
|
29
29
|
spec.add_development_dependency 'pry-byebug'
|
30
30
|
spec.add_development_dependency 'rake', '~> 13.0.3'
|
31
|
+
spec.add_development_dependency 'rollbar'
|
31
32
|
spec.add_development_dependency 'rspec', '~> 3.10.0 '
|
32
33
|
spec.add_development_dependency 'rspec_junit_formatter' # for CircleCI.
|
33
34
|
spec.add_development_dependency 'rubocop', '~> 1.12.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puppeteer-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.32.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- YusukeIwaki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-04-
|
11
|
+
date: 2021-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 13.0.3
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rollbar
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: rspec
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -249,6 +263,8 @@ files:
|
|
249
263
|
- lib/puppeteer/concurrent_ruby_utils.rb
|
250
264
|
- lib/puppeteer/connection.rb
|
251
265
|
- lib/puppeteer/console_message.rb
|
266
|
+
- lib/puppeteer/coverage.rb
|
267
|
+
- lib/puppeteer/css_coverage.rb
|
252
268
|
- lib/puppeteer/custom_query_handler.rb
|
253
269
|
- lib/puppeteer/debug_print.rb
|
254
270
|
- lib/puppeteer/define_async_method.rb
|
@@ -272,6 +288,7 @@ files:
|
|
272
288
|
- lib/puppeteer/frame_manager.rb
|
273
289
|
- lib/puppeteer/geolocation.rb
|
274
290
|
- lib/puppeteer/if_present.rb
|
291
|
+
- lib/puppeteer/js_coverage.rb
|
275
292
|
- lib/puppeteer/js_handle.rb
|
276
293
|
- lib/puppeteer/keyboard.rb
|
277
294
|
- lib/puppeteer/keyboard/key_description.rb
|
@@ -290,6 +307,7 @@ files:
|
|
290
307
|
- lib/puppeteer/page/pdf_options.rb
|
291
308
|
- lib/puppeteer/page/screenshot_options.rb
|
292
309
|
- lib/puppeteer/page/screenshot_task_queue.rb
|
310
|
+
- lib/puppeteer/protocol_stream_reader.rb
|
293
311
|
- lib/puppeteer/puppeteer.rb
|
294
312
|
- lib/puppeteer/query_handler_manager.rb
|
295
313
|
- lib/puppeteer/remote_object.rb
|
@@ -298,6 +316,7 @@ files:
|
|
298
316
|
- lib/puppeteer/target.rb
|
299
317
|
- lib/puppeteer/timeout_settings.rb
|
300
318
|
- lib/puppeteer/touch_screen.rb
|
319
|
+
- lib/puppeteer/tracing.rb
|
301
320
|
- lib/puppeteer/version.rb
|
302
321
|
- lib/puppeteer/viewport.rb
|
303
322
|
- lib/puppeteer/wait_task.rb
|