grover 0.13.2 → 1.0.1

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: ce6455d4d8ede0f5b91972977f8e5d17d8fab13e60096625cd9eb05e759ee8db
4
- data.tar.gz: 12484117509a4f5e06bcce9cd39b95f16f105b662ff204f0a0f45b5a22455b51
3
+ metadata.gz: f2409cbc42fc0fcb80876e1d4238f08cfbeae1d7fc7dada4cb958e8178bafc95
4
+ data.tar.gz: 8189bc362f4979028340f17095c0e9aade0d7950f605fb2062740452316e2b60
5
5
  SHA512:
6
- metadata.gz: b488843267799c52530b27b7a3b8ac8cc76d8767f76062aca8cd32e7560c169d5e8e4d3f4a3498041cd180c4b0487337a51039487846a8a317fb2ecc4499a24a
7
- data.tar.gz: fe4681c363d61d1a50fdedd0bbc0d76efa52b5d7990443e0b5fd7f978639a9ee3907b453a54afc3d595f27e85ca0a8d07ea3c91b52784f3f33914c6e1e31b6bc
6
+ metadata.gz: 0ed3057d89a650c43dbd5c606edcef96e31702cde46a18896af692aaa0b000c2824df2cc90d33e2a8767486940cc4063a2c28bdd609b459579d698fb282465b4
7
+ data.tar.gz: 23ede80fa749b4095cf94733690d398fd5027c33a1d11adccc63fa3300dbc2cf89e466c6ff8e8b63be6f67a3a00eff91d9c3c9124ea98d37ef4ab86df439fb3d
data/lib/grover.rb CHANGED
@@ -33,8 +33,8 @@ class Grover
33
33
  # see https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions
34
34
  #
35
35
  def initialize(url, options = {})
36
- @url = url
37
- @options = OptionsBuilder.new(options, url)
36
+ @url = url.to_s
37
+ @options = OptionsBuilder.new(options, @url)
38
38
  @root_path = @options.delete 'root_path'
39
39
  @front_cover_path = @options.delete 'front_cover_path'
40
40
  @back_cover_path = @options.delete 'back_cover_path'
@@ -50,6 +50,15 @@ class Grover
50
50
  processor.convert :pdf, @url, normalized_options(path: path)
51
51
  end
52
52
 
53
+ #
54
+ # Request URL with provided options and render HTML
55
+ #
56
+ # @return [String] The resulting HTML string
57
+ #
58
+ def to_html
59
+ processor.convert :content, @url, normalized_options(path: nil)
60
+ end
61
+
53
62
  #
54
63
  # Request URL with provided options and create screenshot
55
64
  #
@@ -135,7 +144,7 @@ class Grover
135
144
  end
136
145
 
137
146
  def normalized_options(path:)
138
- normalized_options = Utils.normalize_object @options
147
+ normalized_options = Utils.normalize_object @options, excluding: ['extraHTTPHeaders']
139
148
  normalized_options['path'] = path if path.is_a? ::String
140
149
  normalized_options
141
150
  end
@@ -11,7 +11,7 @@ try {
11
11
  process.stdout.write("[\"ok\"]\n");
12
12
 
13
13
  const _processPage = (async (convertAction, urlOrHtml, options) => {
14
- let browser;
14
+ let browser, errors = [];
15
15
  try {
16
16
  const launchParams = {
17
17
  args: process.env.GROVER_NO_SANDBOX === 'true' ? ['--no-sandbox', '--disable-setuid-sandbox'] : []
@@ -66,12 +66,76 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
66
66
  requestOptions.timeout = timeout;
67
67
  }
68
68
 
69
+ // Setup user agent (if provided)
70
+ const userAgent = options.userAgent; delete options.userAgent;
71
+ if (userAgent !== undefined) {
72
+ await page.setUserAgent(userAgent);
73
+ }
74
+
69
75
  // Setup viewport options (if provided)
70
76
  const viewport = options.viewport; delete options.viewport;
71
77
  if (viewport !== undefined) {
72
78
  await page.setViewport(viewport);
73
79
  }
74
80
 
81
+ // If specified, emulate the media type
82
+ const emulateMedia = options.emulateMedia; delete options.emulateMedia;
83
+ if (emulateMedia !== undefined) {
84
+ if (typeof page.emulateMediaType === 'function') {
85
+ await page.emulateMediaType(emulateMedia);
86
+ } else {
87
+ await page.emulateMedia(emulateMedia);
88
+ }
89
+ }
90
+
91
+ // Emulate the media features, if specified
92
+ const mediaFeatures = options.mediaFeatures; delete options.mediaFeatures;
93
+ if (Array.isArray(mediaFeatures)) {
94
+ page.emulateMediaFeatures(mediaFeatures);
95
+ }
96
+
97
+ // Emulate timezone (if provided)
98
+ const timezone = options.timezone; delete options.timezone;
99
+ if (timezone !== undefined) {
100
+ await page.emulateTimezone(timezone);
101
+ }
102
+
103
+ // Emulate vision deficiency (if provided)
104
+ const visionDeficiency = options.visionDeficiency; delete options.visionDeficiency;
105
+ if (visionDeficiency !== undefined) {
106
+ page.emulateVisionDeficiency(type);
107
+ }
108
+
109
+ // Bypass CSP (content security policy), if provided
110
+ const bypassCSP = options.bypassCSP; delete options.bypassCSP;
111
+ if (bypassCSP !== undefined) {
112
+ page.setBypassCSP(bypassCSP);
113
+ }
114
+
115
+ // Add extra HTTP headers (if provided)
116
+ const extraHTTPHeaders = options.extraHTTPHeaders; delete options.extraHTTPHeaders;
117
+ if (extraHTTPHeaders !== undefined) {
118
+ page.setExtraHTTPHeaders(extraHTTPHeaders);
119
+ }
120
+
121
+ // Set geolocation (if provided)
122
+ const geolocation = options.geolocation; delete options.geolocation;
123
+ if (geolocation !== undefined) {
124
+ page.setGeolocation(geolocation);
125
+ }
126
+
127
+ const raiseOnRequestFailure = options.raiseOnRequestFailure; delete options.raiseOnRequestFailure;
128
+ if (raiseOnRequestFailure) {
129
+ page.on('requestfinished', (request) => {
130
+ if (request.response() && !request.response().ok() && !request.redirectChain().length > 0) {
131
+ errors.push(request);
132
+ }
133
+ });
134
+ page.on('requestfailed', (request) => {
135
+ errors.push(request);
136
+ });
137
+ }
138
+
75
139
  const waitUntil = options.waitUntil; delete options.waitUntil;
76
140
  if (urlOrHtml.match(/^http/i)) {
77
141
  // Request is for a URL, so request it
@@ -82,7 +146,7 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
82
146
  requestOptions.waitUntil = waitUntil || 'networkidle0';
83
147
  await page.setRequestInterception(true);
84
148
  page.once('request', request => {
85
- request.respond({ body: urlOrHtml });
149
+ request.respond({ body: urlOrHtml === '' ? ' ' : urlOrHtml });
86
150
  // Reset the request interception
87
151
  // (we only want to intercept the first request - ie our HTML)
88
152
  page.on('request', request => request.continue());
@@ -91,13 +155,19 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
91
155
  await page.goto(displayUrl || 'http://example.com', requestOptions);
92
156
  }
93
157
 
94
- // If specified, emulate the media type
95
- const emulateMedia = options.emulateMedia; delete options.emulateMedia;
96
- if (emulateMedia !== undefined) {
97
- if (typeof page.emulateMediaType == 'function') {
98
- await page.emulateMediaType(emulateMedia);
99
- } else {
100
- await page.emulateMedia(emulateMedia);
158
+ // add styles (if provided)
159
+ const styleTagOptions = options.styleTagOptions; delete options.styleTagOptions;
160
+ if (Array.isArray(styleTagOptions)) {
161
+ for (const styleTagOption of styleTagOptions) {
162
+ await page.addStyleTag(styleTagOption);
163
+ }
164
+ }
165
+
166
+ // add scripts (if provided)
167
+ const scriptTagOptions = options.scriptTagOptions; delete options.scriptTagOptions;
168
+ if (Array.isArray(scriptTagOptions)) {
169
+ for (const scriptTagOption of scriptTagOptions) {
170
+ await page.addScriptTag(scriptTagOption);
101
171
  }
102
172
  }
103
173
 
@@ -111,7 +181,42 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
111
181
  const waitForSelector = options.waitForSelector; delete options.waitForSelector;
112
182
  const waitForSelectorOptions = options.waitForSelectorOptions; delete options.waitForSelectorOptions;
113
183
  if (waitForSelector !== undefined) {
114
- await page.waitForSelector(waitForSelector, waitForSelectorOptions)
184
+ await page.waitForSelector(waitForSelector, waitForSelectorOptions);
185
+ }
186
+
187
+ // If specified, wait for timeout
188
+ const waitForTimeout = options.waitForTimeout; delete options.waitForTimeout;
189
+ if (waitForTimeout !== undefined) {
190
+ await page.waitForTimeout(waitForTimeout);
191
+ }
192
+
193
+ // If specified, focus on the specified selector
194
+ const focusSelector = options.focus; delete options.focus;
195
+ if (focusSelector !== undefined) {
196
+ await page.focus(focusSelector);
197
+ }
198
+
199
+ // If specified, hover on the specified selector
200
+ const hoverSelector = options.hover; delete options.hover;
201
+ if (hoverSelector !== undefined) {
202
+ await page.hover(hoverSelector);
203
+ }
204
+
205
+ if (errors.length > 0) {
206
+ function RequestFailedError(errors) {
207
+ this.name = "RequestFailedError";
208
+ this.message = errors.map(e => {
209
+ if (e.failure()) {
210
+ return e.failure().errorText + " at " + e.url();
211
+ } else if (e.response() && e.response().status()) {
212
+ return e.response().status() + " " + e.url();
213
+ } else {
214
+ return "UnknownError " + e.url()
215
+ }
216
+ }).join("\n");
217
+ }
218
+ RequestFailedError.prototype = Error.prototype;
219
+ throw new RequestFailedError(errors);
115
220
  }
116
221
 
117
222
  // If we're running puppeteer in headless mode, return the converted PDF
@@ -34,13 +34,13 @@ class Grover
34
34
  def fix_boolean_options!
35
35
  fix_options!(
36
36
  'display_header_footer', 'full_page', 'landscape', 'omit_background', 'prefer_css_page_size',
37
- 'print_background', 'viewport.has_touch', 'viewport.is_landscape', 'viewport.is_mobile'
37
+ 'print_background', 'viewport.has_touch', 'viewport.is_landscape', 'viewport.is_mobile', 'bypass_csp'
38
38
  ) { |value| !FALSE_VALUES.include?(value) }
39
39
  end
40
40
 
41
41
  def fix_integer_options!
42
42
  fix_options!(
43
- 'viewport.height', 'viewport.width',
43
+ 'viewport.height', 'viewport.width', 'wait_for_timeout',
44
44
  &:to_i
45
45
  )
46
46
  end
@@ -48,6 +48,7 @@ class Grover
48
48
  def fix_float_options!
49
49
  fix_options!(
50
50
  'clip.height', 'clip.width', 'clip.x', 'clip.y', 'quality', 'scale', 'viewport.device_scale_factor',
51
+ 'geolocation.latitude', 'geolocation.longitude',
51
52
  &:to_f
52
53
  )
53
54
  end
@@ -19,6 +19,7 @@ class Grover
19
19
  ensure_packages_are_initiated
20
20
  result = call_js_method method, url_or_html, options
21
21
  return unless result
22
+ return result if result.is_a?(String)
22
23
 
23
24
  result['data'].pack('C*')
24
25
  ensure
data/lib/grover/utils.rb CHANGED
@@ -6,7 +6,9 @@ class Grover
6
6
  #
7
7
  class Utils
8
8
  ACRONYMS = {
9
- 'css' => 'CSS'
9
+ 'css' => 'CSS',
10
+ 'csp' => 'CSP',
11
+ 'http' => 'HTTP'
10
12
  }.freeze
11
13
  private_constant :ACRONYMS
12
14
 
@@ -41,11 +43,12 @@ class Grover
41
43
  # Copied from active support
42
44
  # @see active_support/core_ext/hash/keys.rb
43
45
  #
44
- def self.deep_transform_keys_in_object(object, &block)
46
+ def self.deep_transform_keys_in_object(object, excluding: [], &block) # rubocop:disable Metrics/MethodLength
45
47
  case object
46
48
  when Hash
47
49
  object.each_with_object({}) do |(key, value), result|
48
- result[yield(key)] = deep_transform_keys_in_object(value, &block)
50
+ new_key = yield(key)
51
+ result[new_key] = excluding.include?(new_key) ? value : deep_transform_keys_in_object(value, &block)
49
52
  end
50
53
  when Array
51
54
  object.map { |e| deep_transform_keys_in_object(e, &block) }
@@ -80,8 +83,8 @@ class Grover
80
83
  #
81
84
  # Recursively normalizes hash objects with camelized string keys
82
85
  #
83
- def self.normalize_object(object)
84
- deep_transform_keys_in_object(object) { |k| normalize_key(k) }
86
+ def self.normalize_object(object, excluding: [])
87
+ deep_transform_keys_in_object(object, excluding: excluding) { |k| normalize_key(k) }
85
88
  end
86
89
 
87
90
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Grover
4
- VERSION = '0.13.2'
4
+ VERSION = '1.0.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.2
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Bromwich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-05 00:00:00.000000000 Z
11
+ date: 2021-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: combine_pdf
@@ -192,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
192
192
  version: 2.5.0
193
193
  - - "<"
194
194
  - !ruby/object:Gem::Version
195
- version: 2.8.0
195
+ version: 3.1.0
196
196
  required_rubygems_version: !ruby/object:Gem::Requirement
197
197
  requirements:
198
198
  - - ">="