bidi2pdf 0.1.22 → 0.1.23
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 +31 -2
- data/README.md +110 -1
- data/lib/bidi2pdf/bidi/browser_tab.rb +47 -2
- data/lib/bidi2pdf/cli.rb +45 -11
- data/lib/bidi2pdf/launcher.rb +11 -37
- data/lib/bidi2pdf/recipe/runner.rb +1 -0
- data/lib/bidi2pdf/recipe.rb +1 -1
- data/lib/bidi2pdf/schema.rb +13 -1
- data/lib/bidi2pdf/session_runner.rb +5 -1
- data/lib/bidi2pdf/version.rb +1 -1
- data/sig/bidi2pdf/bidi/browser_tab.rbs +23 -0
- data/sig/bidi2pdf/cli.rbs +15 -4
- data/sig/bidi2pdf/launcher.rbs +5 -20
- data/sig/bidi2pdf/recipe.rbs +1 -1
- data/sig/bidi2pdf/schema.rbs +1 -1
- data/sig/bidi2pdf/session_runner.rbs +5 -1
- data/sig/bidi2pdf/version.rbs +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9bff5f993e54cb0f70e6a2c83e022bf29fdef7b0fafc6ce8b641d5ec33aea7ea
|
|
4
|
+
data.tar.gz: e2466bb66703e3a7ac4ca16bfdea22f836c8fff07492e9f44904ecf5bc9c9c63
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6958ef3cc24d5350fdb090598940410f5cd3ab9abef4003f118d52ab61142b45c6427e0225ed4d37a9fa3a23d0dfd033e67a07cc981f891cb2f1f95f7af67612
|
|
7
|
+
data.tar.gz: 74a366dc1c4953bb6e17e8c7578d3ec79cd7a0ea9f1f058f9d353ebdab12b2b046d7e25424b22745081895b09f60a5ca7a701eb29763472b9320e6a564142ad7
|
data/CHANGELOG.md
CHANGED
|
@@ -8,10 +8,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
-
[unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.
|
|
11
|
+
[unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.23..HEAD
|
|
12
12
|
|
|
13
13
|
<!-- generated by git-cliff end -->
|
|
14
14
|
|
|
15
|
+
## [0.1.23] - 2026-10-18
|
|
16
|
+
|
|
17
|
+
### 🐛 Fixed
|
|
18
|
+
- Spell the plugin version as semver
|
|
19
|
+
- Keep screenshot names apart and stop browsers
|
|
20
|
+
- Stop every browser even when one stop fails
|
|
21
|
+
- Keep stdout to the JSON document or the PDF
|
|
22
|
+
- Lay a mobile print out at the intended width
|
|
23
|
+
|
|
24
|
+
### 📝 Docs
|
|
25
|
+
- Explain how to install, pin and update the Claude Code skills
|
|
26
|
+
- Fix the script path and weigh the Chrome sandbox
|
|
27
|
+
|
|
28
|
+
### 🔄 Changed
|
|
29
|
+
- Merge pull request #170 from dieter-medium/feat/claude-skills
|
|
30
|
+
- Merge pull request #169 from dieter-medium/fix/clean-stdout
|
|
31
|
+
- Merge pull request #160 from dieter-medium/dependabot/bundler/main/simplecov-1.3.2
|
|
32
|
+
- Merge pull request #167 from dieter-medium/dependabot/bundler/main/rubocop-1.92.0
|
|
33
|
+
- Merge pull request #168 from dieter-medium/feat/emulate-media
|
|
34
|
+
|
|
35
|
+
### 🔧 Build
|
|
36
|
+
- Bump simplecov from 1.3.1 to 1.3.2
|
|
37
|
+
- Bump rubocop from 1.91.0 to 1.92.0
|
|
38
|
+
|
|
39
|
+
### 🚀 Added
|
|
40
|
+
- Ship Claude Code skills for bidi2pdf
|
|
41
|
+
- Let a tab emulate a CSS media type
|
|
42
|
+
|
|
15
43
|
## [0.1.22] - 2026-10-11
|
|
16
44
|
|
|
17
45
|
### 🐛 Fixed
|
|
@@ -568,7 +596,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
568
596
|
|
|
569
597
|
### 🔄 Released
|
|
570
598
|
|
|
571
|
-
- [unreleased](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.
|
|
599
|
+
- [unreleased](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.23..HEAD)
|
|
600
|
+
- [0.1.23](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.22..v0.1.23)
|
|
572
601
|
- [0.1.22](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.21..v0.1.22)
|
|
573
602
|
- [0.1.21](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.20..v0.1.21)
|
|
574
603
|
- [0.1.20](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.19..v0.1.20)
|
data/README.md
CHANGED
|
@@ -110,6 +110,26 @@ bidi2pdf render \
|
|
|
110
110
|
--log-level debug
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
#### The page as it looks on screen, dark mode, or its mobile version
|
|
114
|
+
|
|
115
|
+
By default Chrome prints with the page's print stylesheet. `--media screen` prints its screen styles
|
|
116
|
+
instead, `--color_scheme dark` its dark mode, `--reduced_motion reduce` with animations off.
|
|
117
|
+
|
|
118
|
+
A narrow page adds the page's mobile layout. A printed page is laid out at the width between its side
|
|
119
|
+
margins, not at the paper's width: page width minus `--margin_left` and `--margin_right`, at about
|
|
120
|
+
37.8 CSS px per cm (96 / 2.54). Without margins of your own Chrome uses 1 cm a side, so a 9.5 cm page
|
|
121
|
+
lays out at about 283 px. For a 360 px phone layout, set the side margins explicitly:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
bidi2pdf render --url https://example.com --output mobile.pdf \
|
|
125
|
+
--media screen --color_scheme dark \
|
|
126
|
+
--page_width 9.525 --page_height 20 --margin_left 0 --margin_right 0
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
8.467 cm without side margins gives 320 px. All page sizes and margins are in centimetres.
|
|
130
|
+
|
|
131
|
+
`bidi2pdf diagnose` takes the same three options, so its screenshot shows the same emulation.
|
|
132
|
+
|
|
113
133
|
---
|
|
114
134
|
|
|
115
135
|
## 🤖 Agent and Automation Usage
|
|
@@ -243,7 +263,7 @@ bidi2pdf run invoice.yml --json # actions, then the PDF, then assertions a
|
|
|
243
263
|
```
|
|
244
264
|
|
|
245
265
|
Actions (`wait_for`, `click`, `evaluate`, `inject_script`, `inject_style`, `set_viewport`,
|
|
246
|
-
`wait_network_idle`) and page assertions (`selector_exists`, `text_present`, `no_console_errors`,
|
|
266
|
+
`wait_network_idle`, `emulate_media`) and page assertions (`selector_exists`, `text_present`, `no_console_errors`,
|
|
247
267
|
`no_network_failures`, `fonts_loaded`) are a thin layer over the same `BrowserTab` methods the
|
|
248
268
|
[Programmatic API](#-programmatic-api) below uses directly. PDF assertions (`page_count`,
|
|
249
269
|
`pdf_text_present`, `pdf_not_blank`) need the `pdf-reader` gem - a recipe using one fails
|
|
@@ -256,6 +276,91 @@ rejected by both `bidi2pdf schema recipe` and `--validate` rather than being sil
|
|
|
256
276
|
`wait_for` needs exactly one of `selector`, `paged_js`, `script` - zero or more than one is
|
|
257
277
|
rejected the same way, before any browser launches.
|
|
258
278
|
|
|
279
|
+
### Claude Code skills
|
|
280
|
+
|
|
281
|
+
The repository ships a [Claude Code](https://claude.com/claude-code) plugin with six skills, so an
|
|
282
|
+
agent uses bidi2pdf the way it is meant to be used instead of guessing options:
|
|
283
|
+
|
|
284
|
+
| Skill | For |
|
|
285
|
+
|---|---|
|
|
286
|
+
| `render-pdf` | a URL or HTML file to PDF with the CLI: order of commands, waits, screen/dark/mobile view, reading the JSON result and exit codes |
|
|
287
|
+
| `pdf-recipe` | a recipe for pages that need steps before printing, validated without a browser |
|
|
288
|
+
| `diagnose-pdf` | a blank, unstyled or wrongly paged PDF: a checklist around `bidi2pdf diagnose` |
|
|
289
|
+
| `remote-chrome` | a shared chromedriver container: settings, sizing, session cleanup, untrusted HTML |
|
|
290
|
+
| `page-screenshots` | review screenshots per page at phone and desktop width, light and dark (with a script) |
|
|
291
|
+
| `pdf-specs` | RSpec specs with the gem's PDF matchers and Chrome container helpers |
|
|
292
|
+
|
|
293
|
+
#### Installing the skills
|
|
294
|
+
|
|
295
|
+
The plugin is not in a central store: this repository is its own Claude Code marketplace
|
|
296
|
+
(`.claude-plugin/marketplace.json`, marketplace name `bidi2pdf`). You add the repository as a
|
|
297
|
+
marketplace once, then install the plugin from it. In a Claude Code session:
|
|
298
|
+
|
|
299
|
+
```text
|
|
300
|
+
/plugin marketplace add dieter-medium/bidi2pdf
|
|
301
|
+
/plugin install bidi2pdf@bidi2pdf
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
`/plugin install` does not install right away: it opens the plugin's details, where you choose the scope
|
|
305
|
+
(just you, the project, or only this checkout) and confirm the installation.
|
|
306
|
+
|
|
307
|
+
Or from a shell, where the install happens at once:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
claude plugin marketplace add dieter-medium/bidi2pdf
|
|
311
|
+
claude plugin install bidi2pdf@bidi2pdf # --scope user (default), project or local
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The skills then load on their own when a task fits them; nothing else needs configuring.
|
|
315
|
+
|
|
316
|
+
**Which version you get.** The plugin's version is the gem's (`claude-plugin/.claude-plugin/plugin.json`),
|
|
317
|
+
and Claude Code installs an update only when that version changes - not with every commit. Adding the
|
|
318
|
+
marketplace as above follows the default branch, so after a release the skills there can already
|
|
319
|
+
describe the next, unreleased gem (the version then reads e.g. `0.1.24-pre`). To stay on the skills of
|
|
320
|
+
the gem you actually use, add the marketplace at its release tag:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
claude plugin marketplace add dieter-medium/bidi2pdf#v0.1.23
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Updating and removing.** Auto-update is off for this marketplace unless you turn it on (`/plugin` >
|
|
327
|
+
Marketplaces). By hand:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
claude plugin marketplace update bidi2pdf # fetch the marketplace again
|
|
331
|
+
claude plugin update bidi2pdf@bidi2pdf # take the new version
|
|
332
|
+
claude plugin uninstall bidi2pdf@bidi2pdf # remove the plugin only
|
|
333
|
+
claude plugin marketplace remove bidi2pdf # remove the marketplace and every plugin installed from it
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**For a whole team.** To give everyone working in a project the skills, commit this to the project's
|
|
337
|
+
`.claude/settings.json` - each member gets them after accepting Claude Code's trust prompt for the
|
|
338
|
+
folder:
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
{
|
|
342
|
+
"extraKnownMarketplaces": {
|
|
343
|
+
"bidi2pdf": { "source": { "source": "github", "repo": "dieter-medium/bidi2pdf" } }
|
|
344
|
+
},
|
|
345
|
+
"enabledPlugins": { "bidi2pdf@bidi2pdf": true }
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
`claude plugin marketplace add dieter-medium/bidi2pdf --scope project` and
|
|
350
|
+
`claude plugin install bidi2pdf@bidi2pdf --scope project` write the same entries for you.
|
|
351
|
+
|
|
352
|
+
**From a checkout, without installing** (to try a change to a skill):
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
claude --plugin-dir ./claude-plugin # for this one session
|
|
356
|
+
claude plugin validate ./claude-plugin --strict # check the plugin's structure
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
The skills point at `bidi2pdf help <command>` and `bidi2pdf schema <kind>` for exact options and
|
|
360
|
+
shapes; a spec (`spec/unit/claude_plugin_spec.rb`) checks every command, option, error code, recipe and
|
|
361
|
+
method they name against the gem, so they cannot drift from it. The plugin is not part of the gem
|
|
362
|
+
itself.
|
|
363
|
+
|
|
259
364
|
---
|
|
260
365
|
|
|
261
366
|
## 🧠 Programmatic API
|
|
@@ -350,6 +455,10 @@ tab.navigate_to "https://example.com/invoice/14432423"
|
|
|
350
455
|
# Printing HTML you do not trust? Switch its scripts off before navigating:
|
|
351
456
|
# tab.disable_script_execution
|
|
352
457
|
|
|
458
|
+
# Print the screen styles in dark mode instead of the print stylesheet (holds for later
|
|
459
|
+
# navigations in this tab; tab.emulate_media without arguments ends it):
|
|
460
|
+
# tab.emulate_media(media: "screen", color_scheme: "dark")
|
|
461
|
+
|
|
353
462
|
tab.wait_until_network_idle
|
|
354
463
|
tab.print("my.pdf")
|
|
355
464
|
|
|
@@ -41,6 +41,13 @@ module Bidi2pdf
|
|
|
41
41
|
class BrowserTab
|
|
42
42
|
include JsLoggerHelper
|
|
43
43
|
|
|
44
|
+
# The values #emulate_media accepts, by argument - what Chrome's Emulation.setEmulatedMedia knows.
|
|
45
|
+
EMULATED_MEDIA = {
|
|
46
|
+
media: %w[screen print],
|
|
47
|
+
color_scheme: %w[light dark],
|
|
48
|
+
reduced_motion: %w[reduce no-preference]
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
44
51
|
# @return [Object] The WebSocket client.
|
|
45
52
|
attr_reader :client
|
|
46
53
|
|
|
@@ -439,7 +446,9 @@ module Bidi2pdf
|
|
|
439
446
|
|
|
440
447
|
block.call(pdf_base64) if block_given?
|
|
441
448
|
|
|
442
|
-
return
|
|
449
|
+
# The block's value, not a `return`: returning out of the instrumented block would
|
|
450
|
+
# skip the print event, and with it every subscriber (ResultCollector, --output -).
|
|
451
|
+
outputfile || block_given? ? nil : pdf_base64
|
|
443
452
|
else
|
|
444
453
|
Bidi2pdf.logger.error "Error printing: #{response}"
|
|
445
454
|
end
|
|
@@ -480,7 +489,8 @@ module Bidi2pdf
|
|
|
480
489
|
|
|
481
490
|
block.call(png_base64) if block_given?
|
|
482
491
|
|
|
483
|
-
|
|
492
|
+
# The block's value, not a `return` - see #print.
|
|
493
|
+
outputfile || block_given? ? nil : png_base64
|
|
484
494
|
else
|
|
485
495
|
Bidi2pdf.logger.error "Error capturing screenshot: #{response}"
|
|
486
496
|
end
|
|
@@ -513,8 +523,43 @@ module Bidi2pdf
|
|
|
513
523
|
client.send_cmd_and_wait(cmd) { |response| response }
|
|
514
524
|
end
|
|
515
525
|
|
|
526
|
+
# Emulates a CSS media type and media features for this tab, through Chrome's own
|
|
527
|
+
# Emulation.setEmulatedMedia (CDP, via goog:cdp.sendCommand). It holds for every later
|
|
528
|
+
# navigation in the tab and for printing: +media: "screen"+ prints a page with its screen styles
|
|
529
|
+
# instead of its print stylesheet, +color_scheme: "dark"+ in its dark mode. On a narrow page the
|
|
530
|
+
# screen media type prints a page's mobile layout: the layout width is the page width minus the
|
|
531
|
+
# side margins (Chrome's default is 1 cm each), at 96 / 2.54 CSS px per cm - e.g.
|
|
532
|
+
# +page: { width: 9.525 }, margin: { left: 0, right: 0 }+ lays out at 360 px. Each call replaces
|
|
533
|
+
# the previous emulation, so a media type or feature it omits is reset; a call without arguments
|
|
534
|
+
# ends it.
|
|
535
|
+
#
|
|
536
|
+
# @param [String, Symbol, nil] media "screen" or "print"; nil leaves it to the browser.
|
|
537
|
+
# @param [String, Symbol, nil] color_scheme "light" or "dark" (prefers-color-scheme).
|
|
538
|
+
# @param [String, Symbol, nil] reduced_motion "reduce" or "no-preference" (prefers-reduced-motion).
|
|
539
|
+
# @raise [ArgumentError] for a value Chrome does not know, before anything is sent.
|
|
540
|
+
def emulate_media(media: nil, color_scheme: nil, reduced_motion: nil)
|
|
541
|
+
params = emulated_media_params(media: media, color_scheme: color_scheme, reduced_motion: reduced_motion)
|
|
542
|
+
cmd = Commands::CdpSendCommand.new(method: "Emulation.setEmulatedMedia", params: params, session: cdp_session)
|
|
543
|
+
client.send_cmd_and_wait(cmd) { |response| response }
|
|
544
|
+
end
|
|
545
|
+
|
|
516
546
|
private
|
|
517
547
|
|
|
548
|
+
def emulated_media_params(**values)
|
|
549
|
+
values.each { |name, value| check_emulated_media(name, value) }
|
|
550
|
+
|
|
551
|
+
features = { "prefers-color-scheme" => values[:color_scheme], "prefers-reduced-motion" => values[:reduced_motion] }
|
|
552
|
+
.filter_map { |name, value| { name: name, value: value.to_s } unless value.nil? }
|
|
553
|
+
{ media: values[:media].to_s, features: features }
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def check_emulated_media(name, value)
|
|
557
|
+
allowed = EMULATED_MEDIA.fetch(name)
|
|
558
|
+
return if value.nil? || allowed.include?(value.to_s)
|
|
559
|
+
|
|
560
|
+
raise ArgumentError, "#{name} must be one of #{allowed.join(", ")}, got #{value.inspect}"
|
|
561
|
+
end
|
|
562
|
+
|
|
518
563
|
def build_command_and_extractor(print_options)
|
|
519
564
|
cmd_type = (print_options.delete(:cmd_type) || :bidi).to_sym
|
|
520
565
|
|
data/lib/bidi2pdf/cli.rb
CHANGED
|
@@ -78,10 +78,10 @@ module Bidi2pdf
|
|
|
78
78
|
option :manifest, type: :string, desc: "Write a render manifest (see `bidi2pdf schema manifest`) to this file"
|
|
79
79
|
|
|
80
80
|
option :background, type: :boolean, default: true, desc: "Print background graphics"
|
|
81
|
-
option :margin_top, type: :numeric, default: 1.0, desc: "Top margin in
|
|
82
|
-
option :margin_bottom, type: :numeric, default: 1.0, desc: "Bottom margin in
|
|
83
|
-
option :margin_left, type: :numeric, default: 1.0, desc: "Left margin in
|
|
84
|
-
option :margin_right, type: :numeric, default: 1.0, desc: "Right margin in
|
|
81
|
+
option :margin_top, type: :numeric, default: 1.0, desc: "Top margin in cm"
|
|
82
|
+
option :margin_bottom, type: :numeric, default: 1.0, desc: "Bottom margin in cm"
|
|
83
|
+
option :margin_left, type: :numeric, default: 1.0, desc: "Left margin in cm"
|
|
84
|
+
option :margin_right, type: :numeric, default: 1.0, desc: "Right margin in cm"
|
|
85
85
|
option :orientation, type: :string, default: "portrait", enum: %w[portrait landscape], desc: "Page orientation"
|
|
86
86
|
option :page_width, type: :numeric, default: 21.59, desc: "Page width in cm (min 0.0352)"
|
|
87
87
|
option :page_height, type: :numeric, default: 27.94, desc: "Page height in cm (min 0.0352)"
|
|
@@ -90,6 +90,9 @@ module Bidi2pdf
|
|
|
90
90
|
option :shrink_to_fit, type: :boolean, default: true, desc: "Shrink content to fit page"
|
|
91
91
|
option :generate_tagged_pdf, type: :boolean, default: false, desc: "Generate tagged PDF"
|
|
92
92
|
option :generate_document_outline, type: :boolean, default: false, desc: "Generate document outline"
|
|
93
|
+
option :media, type: :string, enum: %w[screen print], desc: "Emulate a CSS media type; screen prints the page with its screen styles. For its mobile layout, narrow the page: the layout width is --page_width minus the side margins, 9.525 cm with --margin_left 0 --margin_right 0 = 360 CSS px"
|
|
94
|
+
option :color_scheme, type: :string, enum: %w[light dark], desc: "Emulate prefers-color-scheme"
|
|
95
|
+
option :reduced_motion, type: :string, enum: %w[reduce no-preference], desc: "Emulate prefers-reduced-motion"
|
|
93
96
|
|
|
94
97
|
class << self
|
|
95
98
|
def exit_on_failure?
|
|
@@ -152,6 +155,9 @@ module Bidi2pdf
|
|
|
152
155
|
option :json, type: :boolean, default: false, desc: "Emit a single structured JSON document (see `bidi2pdf schema diagnose`) instead of a human-readable summary"
|
|
153
156
|
option :json_stream, type: :boolean, default: false, desc: "Emit one JSON progress event per line to stderr as diagnostics run, with or without --json (see `bidi2pdf schema event`)"
|
|
154
157
|
option :screenshot, type: :string, desc: "Also capture a screenshot to this PNG file"
|
|
158
|
+
option :media, type: :string, enum: %w[screen print], desc: "Emulate a CSS media type; screen prints the page with its screen styles. For its mobile layout, narrow the page: the layout width is --page_width minus the side margins, 9.525 cm with --margin_left 0 --margin_right 0 = 360 CSS px"
|
|
159
|
+
option :color_scheme, type: :string, enum: %w[light dark], desc: "Emulate prefers-color-scheme"
|
|
160
|
+
option :reduced_motion, type: :string, enum: %w[reduce no-preference], desc: "Emulate prefers-reduced-motion"
|
|
155
161
|
option :pdf, type: :string, desc: "Also render a PDF to this file and report its page count / blank-check"
|
|
156
162
|
|
|
157
163
|
def diagnose
|
|
@@ -307,9 +313,11 @@ module Bidi2pdf
|
|
|
307
313
|
emit_structured_render_output(result, collector)
|
|
308
314
|
|
|
309
315
|
exit_for_result(result)
|
|
316
|
+
ensure
|
|
317
|
+
stop_browsers
|
|
310
318
|
end
|
|
311
319
|
ensure
|
|
312
|
-
|
|
320
|
+
stop_browsers
|
|
313
321
|
stream&.unsubscribe
|
|
314
322
|
cleanup_stdin_tempfile
|
|
315
323
|
end
|
|
@@ -369,9 +377,11 @@ module Bidi2pdf
|
|
|
369
377
|
$stdout.puts(diagnose_payload(result, diagnostics).to_json)
|
|
370
378
|
|
|
371
379
|
exit_for_result(result)
|
|
380
|
+
ensure
|
|
381
|
+
stop_browsers
|
|
372
382
|
end
|
|
373
383
|
ensure
|
|
374
|
-
|
|
384
|
+
stop_browsers
|
|
375
385
|
stream&.unsubscribe
|
|
376
386
|
cleanup_stdin_tempfile
|
|
377
387
|
end
|
|
@@ -413,7 +423,6 @@ module Bidi2pdf
|
|
|
413
423
|
# --- run - Loader/Validator run before any browser work, both in --validate and in a real
|
|
414
424
|
# run, so an invalid recipe never launches one.
|
|
415
425
|
|
|
416
|
-
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
417
426
|
def perform_human_run(recipe_path)
|
|
418
427
|
recipe = load_and_validate_recipe(recipe_path)
|
|
419
428
|
return puts "Recipe '#{recipe_path}' is valid." if merged_options[:validate]
|
|
@@ -448,12 +457,12 @@ module Bidi2pdf
|
|
|
448
457
|
rescue Bidi2pdf::Error => e
|
|
449
458
|
raise Thor::Error, e.message
|
|
450
459
|
ensure
|
|
451
|
-
|
|
460
|
+
stop_browsers
|
|
452
461
|
stream&.unsubscribe
|
|
453
462
|
cleanup_stdin_tempfile
|
|
454
463
|
end
|
|
455
464
|
|
|
456
|
-
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
465
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
457
466
|
def perform_structured_run(recipe_path)
|
|
458
467
|
recipe = load_and_validate_recipe(recipe_path)
|
|
459
468
|
|
|
@@ -490,12 +499,14 @@ module Bidi2pdf
|
|
|
490
499
|
$stdout.puts(payload.to_json)
|
|
491
500
|
|
|
492
501
|
exit_for_result(result)
|
|
502
|
+
ensure
|
|
503
|
+
stop_browsers
|
|
493
504
|
end
|
|
494
505
|
rescue Bidi2pdf::InvalidRecipeError, Bidi2pdf::PdfInspectionUnavailableError, Bidi2pdf::InvalidPrintOptionError => e
|
|
495
506
|
$stdout.puts Bidi2pdf::Result.failure(command: "run", error: ErrorCodes.describe(e)).to_json
|
|
496
507
|
exit(Bidi2pdf::ExitCodes.for(ErrorCodes.for(e)))
|
|
497
508
|
ensure
|
|
498
|
-
|
|
509
|
+
stop_browsers
|
|
499
510
|
stream&.unsubscribe
|
|
500
511
|
cleanup_stdin_tempfile
|
|
501
512
|
end
|
|
@@ -510,6 +521,22 @@ module Bidi2pdf
|
|
|
510
521
|
recipe.source["url"] || recipe.source["file"] || "(stdin)"
|
|
511
522
|
end
|
|
512
523
|
|
|
524
|
+
# Stops whatever browser this command started, once. The structured paths call it inside
|
|
525
|
+
# reserve_stdout_for_machine_output, while the loggers still write to stderr: stopping logs
|
|
526
|
+
# ("WebSocket closed"), and after the block those lines would land on stdout behind the JSON
|
|
527
|
+
# document or the PDF bytes. Each browser is stopped on its own, and a failing stop is logged,
|
|
528
|
+
# not raised: this runs in an ensure, where raising would replace the command's own result and
|
|
529
|
+
# exit status.
|
|
530
|
+
def stop_browsers
|
|
531
|
+
launchers = [(@launcher if defined?(@launcher)), (@run_launcher if defined?(@run_launcher))].compact
|
|
532
|
+
@launcher = @run_launcher = nil
|
|
533
|
+
launchers.each do |launcher|
|
|
534
|
+
launcher.stop
|
|
535
|
+
rescue StandardError => e
|
|
536
|
+
Bidi2pdf.logger.error "Stopping the browser failed: #{e.class}: #{e.message}"
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
|
|
513
540
|
def with_recipe_tab(recipe)
|
|
514
541
|
@run_launcher = launcher_for_recipe(recipe)
|
|
515
542
|
tab = @run_launcher.diagnose
|
|
@@ -818,11 +845,18 @@ module Bidi2pdf
|
|
|
818
845
|
wait_network_idle: merged_options[:wait_network_idle],
|
|
819
846
|
print_options: print_options,
|
|
820
847
|
network_log_format: merged_options[:network_log_format],
|
|
821
|
-
chrome_args: chrome_args
|
|
848
|
+
chrome_args: chrome_args,
|
|
849
|
+
emulate_media: emulate_media_options
|
|
822
850
|
)
|
|
823
851
|
end
|
|
824
852
|
end
|
|
825
853
|
|
|
854
|
+
# The --media/--color_scheme/--reduced_motion choices for BrowserTab#emulate_media; empty when none
|
|
855
|
+
# is given, so the browser keeps its own.
|
|
856
|
+
def emulate_media_options
|
|
857
|
+
{ media: merged_options[:media], color_scheme: merged_options[:color_scheme], reduced_motion: merged_options[:reduced_motion] }.compact
|
|
858
|
+
end
|
|
859
|
+
|
|
826
860
|
def configure
|
|
827
861
|
Bidi2pdf.configure do |config|
|
|
828
862
|
config.logger.level = log_level
|
data/lib/bidi2pdf/launcher.rb
CHANGED
|
@@ -35,45 +35,31 @@ module Bidi2pdf
|
|
|
35
35
|
# @param [Hash] print_options Options for printing the page. Defaults to an empty hash.
|
|
36
36
|
# @param [String, nil] remote_browser_url The URL of a remote browser to connect to. Defaults to nil.
|
|
37
37
|
# @param [Symbol] network_log_format The format for network logs. Defaults to :console.
|
|
38
|
+
# @param [Hash] emulate_media Media type and features for BrowserTab#emulate_media (media:,
|
|
39
|
+
# color_scheme:, reduced_motion:). Defaults to none.
|
|
38
40
|
class Launcher
|
|
39
41
|
# rubocop:disable Metrics/ParameterLists
|
|
40
42
|
def initialize(url:, inputfile:, output:, cookies:, headers:, auth:, headless: true, port: 0, wait_window_loaded: false,
|
|
41
43
|
wait_network_idle: false, print_options: {}, remote_browser_url: nil, network_log_format: :console,
|
|
42
|
-
chrome_args: Bidi::Session::DEFAULT_CHROME_ARGS)
|
|
43
|
-
|
|
44
|
-
@
|
|
44
|
+
chrome_args: Bidi::Session::DEFAULT_CHROME_ARGS, emulate_media: {})
|
|
45
|
+
# What both runners get, unchanged - SessionRunner's own keyword arguments.
|
|
46
|
+
@runner_options = {
|
|
47
|
+
url: url, inputfile: inputfile, output: output, cookies: cookies, headers: headers, auth: auth,
|
|
48
|
+
wait_window_loaded: wait_window_loaded, wait_network_idle: wait_network_idle, print_options: print_options || {},
|
|
49
|
+
network_log_format: network_log_format, emulate_media: emulate_media || {}
|
|
50
|
+
}
|
|
45
51
|
@port = port
|
|
46
52
|
@headless = headless
|
|
47
|
-
@output = output
|
|
48
|
-
@cookies = cookies
|
|
49
|
-
@headers = headers
|
|
50
|
-
@auth = auth
|
|
51
53
|
@manager = nil
|
|
52
|
-
@wait_window_loaded = wait_window_loaded
|
|
53
|
-
@wait_network_idle = wait_network_idle
|
|
54
|
-
@print_options = print_options || {}
|
|
55
54
|
@remote_browser_url = remote_browser_url
|
|
56
55
|
@custom_session = nil
|
|
57
|
-
@network_log_format = network_log_format
|
|
58
56
|
@chrome_args = chrome_args
|
|
59
57
|
end
|
|
60
58
|
|
|
61
59
|
# rubocop:enable Metrics/ParameterLists
|
|
62
60
|
|
|
63
61
|
def launch
|
|
64
|
-
runner = SessionRunner.new(
|
|
65
|
-
session: session,
|
|
66
|
-
url: @url,
|
|
67
|
-
inputfile: @inputfile,
|
|
68
|
-
output: @output,
|
|
69
|
-
cookies: @cookies,
|
|
70
|
-
headers: @headers,
|
|
71
|
-
auth: @auth,
|
|
72
|
-
wait_window_loaded: @wait_window_loaded,
|
|
73
|
-
wait_network_idle: @wait_network_idle,
|
|
74
|
-
print_options: @print_options,
|
|
75
|
-
network_log_format: @network_log_format
|
|
76
|
-
)
|
|
62
|
+
runner = SessionRunner.new(session: session, **@runner_options)
|
|
77
63
|
runner.run
|
|
78
64
|
end
|
|
79
65
|
|
|
@@ -83,19 +69,7 @@ module Bidi2pdf
|
|
|
83
69
|
#
|
|
84
70
|
# @return [Bidi2pdf::Bidi::BrowserTab] the navigated tab.
|
|
85
71
|
def diagnose
|
|
86
|
-
@diagnose_runner = SessionRunner.new(
|
|
87
|
-
session: session,
|
|
88
|
-
url: @url,
|
|
89
|
-
inputfile: @inputfile,
|
|
90
|
-
output: @output,
|
|
91
|
-
cookies: @cookies,
|
|
92
|
-
headers: @headers,
|
|
93
|
-
auth: @auth,
|
|
94
|
-
wait_window_loaded: @wait_window_loaded,
|
|
95
|
-
wait_network_idle: @wait_network_idle,
|
|
96
|
-
print_options: @print_options,
|
|
97
|
-
network_log_format: @network_log_format
|
|
98
|
-
)
|
|
72
|
+
@diagnose_runner = SessionRunner.new(session: session, **@runner_options)
|
|
99
73
|
@diagnose_runner.run_diagnose
|
|
100
74
|
end
|
|
101
75
|
|
|
@@ -90,6 +90,7 @@ module Bidi2pdf
|
|
|
90
90
|
when "inject_style" then @tab.inject_style(url: opts["url"], content: opts["content"], id: opts["id"])
|
|
91
91
|
when "set_viewport" then @tab.set_viewport(width: opts["width"], height: opts["height"], device_pixel_ratio: opts["device_pixel_ratio"])
|
|
92
92
|
when "wait_network_idle" then @tab.wait_until_network_idle(timeout: opts.fetch("timeout", 10))
|
|
93
|
+
when "emulate_media" then @tab.emulate_media(media: opts["media"], color_scheme: opts["color_scheme"], reduced_motion: opts["reduced_motion"])
|
|
93
94
|
end
|
|
94
95
|
end
|
|
95
96
|
|
data/lib/bidi2pdf/recipe.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Bidi2pdf
|
|
|
12
12
|
# launches) -> Validator -> Runner.
|
|
13
13
|
class Recipe
|
|
14
14
|
KNOWN_TOP_LEVEL_KEYS = %w[version source browser headers cookies auth actions assert print output].freeze
|
|
15
|
-
KNOWN_ACTIONS = %w[wait_for click evaluate inject_script inject_style set_viewport wait_network_idle].freeze
|
|
15
|
+
KNOWN_ACTIONS = %w[wait_for click evaluate inject_script inject_style set_viewport wait_network_idle emulate_media].freeze
|
|
16
16
|
PAGE_ASSERTIONS = %w[selector_exists text_present no_console_errors no_network_failures fonts_loaded].freeze
|
|
17
17
|
PDF_ASSERTIONS = %w[page_count pdf_text_present pdf_not_blank].freeze
|
|
18
18
|
KNOWN_ASSERTIONS = (PAGE_ASSERTIONS + PDF_ASSERTIONS).freeze
|
data/lib/bidi2pdf/schema.rb
CHANGED
|
@@ -219,7 +219,7 @@ module Bidi2pdf
|
|
|
219
219
|
}.freeze
|
|
220
220
|
|
|
221
221
|
RECIPE_ACTIONS = {
|
|
222
|
-
"description" => "one of the
|
|
222
|
+
"description" => "one of the 8 known actions - a thin dispatch over BrowserTab's own methods, see README's " \
|
|
223
223
|
"Agent and Automation Usage section",
|
|
224
224
|
"oneOf" => [
|
|
225
225
|
{
|
|
@@ -271,6 +271,18 @@ module Bidi2pdf
|
|
|
271
271
|
"type" => "object", "required" => %w[wait_network_idle], "additionalProperties" => false,
|
|
272
272
|
"properties" => { "wait_network_idle" => { "type" => "object", "additionalProperties" => false,
|
|
273
273
|
"properties" => { "timeout" => { "type" => "number" } } } }
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"type" => "object", "required" => %w[emulate_media], "additionalProperties" => false,
|
|
277
|
+
"properties" => {
|
|
278
|
+
"emulate_media" => {
|
|
279
|
+
"type" => "object", "additionalProperties" => false,
|
|
280
|
+
"description" => "the CSS media type and media features the page is shown and printed with, from here on; " \
|
|
281
|
+
"media: screen prints the screen styles; on a narrow print page (layout width = page width minus the side margins, " \
|
|
282
|
+
"9.525 cm without side margins = 360 CSS px) the mobile layout",
|
|
283
|
+
"properties" => Bidi2pdf::Bidi::BrowserTab::EMULATED_MEDIA.to_h { |name, values| [name.to_s, { "type" => "string", "enum" => values }] }
|
|
284
|
+
}
|
|
285
|
+
}
|
|
274
286
|
}
|
|
275
287
|
]
|
|
276
288
|
}.freeze
|
|
@@ -33,10 +33,12 @@ module Bidi2pdf
|
|
|
33
33
|
# @param [Boolean] wait_network_idle Whether to wait for the network to become idle. Defaults to false.
|
|
34
34
|
# @param [Hash] print_options Options for printing the page. Defaults to an empty hash.
|
|
35
35
|
# @param [Symbol] network_log_format The format for network logs. Defaults to :console.
|
|
36
|
+
# @param [Hash] emulate_media Media type and features for BrowserTab#emulate_media (media:,
|
|
37
|
+
# color_scheme:, reduced_motion:), applied before navigating. Defaults to none.
|
|
36
38
|
class SessionRunner
|
|
37
39
|
# rubocop: disable Metrics/ParameterLists
|
|
38
40
|
def initialize(session:, url:, inputfile:, output:, cookies: {}, headers: {}, auth: {}, wait_window_loaded: false,
|
|
39
|
-
wait_network_idle: false, print_options: {}, network_log_format: :console)
|
|
41
|
+
wait_network_idle: false, print_options: {}, network_log_format: :console, emulate_media: {})
|
|
40
42
|
@session = session
|
|
41
43
|
@url = url
|
|
42
44
|
@inputfile = inputfile
|
|
@@ -48,6 +50,7 @@ module Bidi2pdf
|
|
|
48
50
|
@wait_network_idle = wait_network_idle
|
|
49
51
|
@print_options = print_options || {}
|
|
50
52
|
@network_log_format = network_log_format
|
|
53
|
+
@emulate_media = emulate_media || {}
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
# rubocop: enable Metrics/ParameterLists
|
|
@@ -100,6 +103,7 @@ module Bidi2pdf
|
|
|
100
103
|
|
|
101
104
|
add_headers
|
|
102
105
|
add_basic_auth
|
|
106
|
+
@tab.emulate_media(**@emulate_media) if @emulate_media.any?
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
def add_cookies(tab)
|
data/lib/bidi2pdf/version.rb
CHANGED
|
@@ -56,6 +56,9 @@ module Bidi2pdf
|
|
|
56
56
|
|
|
57
57
|
include JsLoggerHelper
|
|
58
58
|
|
|
59
|
+
# The values #emulate_media accepts, by argument - what Chrome's Emulation.setEmulatedMedia knows.
|
|
60
|
+
EMULATED_MEDIA: { media: ::Array["screen" | "print"], color_scheme: ::Array["light" | "dark"], reduced_motion: ::Array["reduce" | "no-preference"] }
|
|
61
|
+
|
|
59
62
|
# @return [Object] The WebSocket client.
|
|
60
63
|
attr_reader client: untyped
|
|
61
64
|
|
|
@@ -238,8 +241,28 @@ module Bidi2pdf
|
|
|
238
241
|
# @param [Boolean] disabled Defaults to true.
|
|
239
242
|
def disable_script_execution: (?disabled: bool) -> untyped
|
|
240
243
|
|
|
244
|
+
# Emulates a CSS media type and media features for this tab, through Chrome's own
|
|
245
|
+
# Emulation.setEmulatedMedia (CDP, via goog:cdp.sendCommand). It holds for every later
|
|
246
|
+
# navigation in the tab and for printing: +media: "screen"+ prints a page with its screen styles
|
|
247
|
+
# instead of its print stylesheet, +color_scheme: "dark"+ in its dark mode. On a narrow page the
|
|
248
|
+
# screen media type prints a page's mobile layout: the layout width is the page width minus the
|
|
249
|
+
# side margins (Chrome's default is 1 cm each), at 96 / 2.54 CSS px per cm - e.g.
|
|
250
|
+
# +page: { width: 9.525 }, margin: { left: 0, right: 0 }+ lays out at 360 px. Each call replaces
|
|
251
|
+
# the previous emulation, so a media type or feature it omits is reset; a call without arguments
|
|
252
|
+
# ends it.
|
|
253
|
+
#
|
|
254
|
+
# @param [String, Symbol, nil] media "screen" or "print"; nil leaves it to the browser.
|
|
255
|
+
# @param [String, Symbol, nil] color_scheme "light" or "dark" (prefers-color-scheme).
|
|
256
|
+
# @param [String, Symbol, nil] reduced_motion "reduce" or "no-preference" (prefers-reduced-motion).
|
|
257
|
+
# @raise [ArgumentError] for a value Chrome does not know, before anything is sent.
|
|
258
|
+
def emulate_media: (?media: untyped?, ?color_scheme: untyped?, ?reduced_motion: untyped?) -> untyped
|
|
259
|
+
|
|
241
260
|
private
|
|
242
261
|
|
|
262
|
+
def emulated_media_params: (**untyped values) -> { media: untyped, features: untyped }
|
|
263
|
+
|
|
264
|
+
def check_emulated_media: (untyped name, untyped value) -> (nil | untyped)
|
|
265
|
+
|
|
243
266
|
def build_command_and_extractor: (untyped print_options) -> ::Array[untyped]
|
|
244
267
|
|
|
245
268
|
def cdp_session: () -> untyped
|
data/sig/bidi2pdf/cli.rbs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Bidi2pdf
|
|
2
2
|
# rubocop:disable Metrics/AbcSize
|
|
3
3
|
class CLI < Thor
|
|
4
|
+
@launcher: untyped
|
|
5
|
+
|
|
4
6
|
@run_launcher: untyped
|
|
5
7
|
|
|
6
8
|
@render_input_path: untyped
|
|
@@ -9,8 +11,6 @@ module Bidi2pdf
|
|
|
9
11
|
|
|
10
12
|
@stdin_tempfile: untyped
|
|
11
13
|
|
|
12
|
-
@launcher: untyped
|
|
13
|
-
|
|
14
14
|
include JsonOutput
|
|
15
15
|
|
|
16
16
|
include SessionCommands
|
|
@@ -55,16 +55,23 @@ module Bidi2pdf
|
|
|
55
55
|
|
|
56
56
|
def capture_diagnose_pdf: (untyped tab) -> (nil | { path: untyped, pages: untyped, not_blank: untyped })
|
|
57
57
|
|
|
58
|
-
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
59
58
|
def perform_human_run: (untyped recipe_path) -> untyped
|
|
60
59
|
|
|
61
|
-
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
60
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
62
61
|
def perform_structured_run: (untyped recipe_path) -> untyped
|
|
63
62
|
|
|
64
63
|
def load_and_validate_recipe: (untyped recipe_path) -> untyped
|
|
65
64
|
|
|
66
65
|
def recipe_requested_url: (untyped recipe) -> untyped
|
|
67
66
|
|
|
67
|
+
# Stops whatever browser this command started, once. The structured paths call it inside
|
|
68
|
+
# reserve_stdout_for_machine_output, while the loggers still write to stderr: stopping logs
|
|
69
|
+
# ("WebSocket closed"), and after the block those lines would land on stdout behind the JSON
|
|
70
|
+
# document or the PDF bytes. Each browser is stopped on its own, and a failing stop is logged,
|
|
71
|
+
# not raised: this runs in an ensure, where raising would replace the command's own result and
|
|
72
|
+
# exit status.
|
|
73
|
+
def stop_browsers: () -> untyped
|
|
74
|
+
|
|
68
75
|
def with_recipe_tab: (untyped recipe) { (untyped) -> untyped } -> untyped
|
|
69
76
|
|
|
70
77
|
# A failing action/assertion raises Recipe::Runner::StepFailure (its own entries plus the real
|
|
@@ -144,6 +151,10 @@ module Bidi2pdf
|
|
|
144
151
|
|
|
145
152
|
def launcher: () -> untyped
|
|
146
153
|
|
|
154
|
+
# The --media/--color_scheme/--reduced_motion choices for BrowserTab#emulate_media; empty when none
|
|
155
|
+
# is given, so the browser keeps its own.
|
|
156
|
+
def emulate_media_options: () -> untyped
|
|
157
|
+
|
|
147
158
|
def configure: () -> untyped
|
|
148
159
|
|
|
149
160
|
def log_level: () -> untyped
|
data/sig/bidi2pdf/launcher.rbs
CHANGED
|
@@ -29,43 +29,28 @@ module Bidi2pdf
|
|
|
29
29
|
# @param [Hash] print_options Options for printing the page. Defaults to an empty hash.
|
|
30
30
|
# @param [String, nil] remote_browser_url The URL of a remote browser to connect to. Defaults to nil.
|
|
31
31
|
# @param [Symbol] network_log_format The format for network logs. Defaults to :console.
|
|
32
|
+
# @param [Hash] emulate_media Media type and features for BrowserTab#emulate_media (media:,
|
|
33
|
+
# color_scheme:, reduced_motion:). Defaults to none.
|
|
32
34
|
class Launcher
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@inputfile: untyped
|
|
35
|
+
# What both runners get, unchanged - SessionRunner's own keyword arguments.
|
|
36
|
+
@runner_options: untyped
|
|
36
37
|
|
|
37
38
|
@port: untyped
|
|
38
39
|
|
|
39
40
|
@headless: untyped
|
|
40
41
|
|
|
41
|
-
@output: untyped
|
|
42
|
-
|
|
43
|
-
@cookies: untyped
|
|
44
|
-
|
|
45
|
-
@headers: untyped
|
|
46
|
-
|
|
47
|
-
@auth: untyped
|
|
48
|
-
|
|
49
42
|
@manager: untyped
|
|
50
43
|
|
|
51
|
-
@wait_window_loaded: untyped
|
|
52
|
-
|
|
53
|
-
@wait_network_idle: untyped
|
|
54
|
-
|
|
55
|
-
@print_options: untyped
|
|
56
|
-
|
|
57
44
|
@remote_browser_url: untyped
|
|
58
45
|
|
|
59
46
|
@custom_session: untyped
|
|
60
47
|
|
|
61
|
-
@network_log_format: untyped
|
|
62
|
-
|
|
63
48
|
@chrome_args: untyped
|
|
64
49
|
|
|
65
50
|
@diagnose_runner: untyped
|
|
66
51
|
|
|
67
52
|
# rubocop:disable Metrics/ParameterLists
|
|
68
|
-
def initialize: (url: untyped, inputfile: untyped, output: untyped, cookies: untyped, headers: untyped, auth: untyped, ?headless: bool, ?port: ::Integer, ?wait_window_loaded: bool, ?wait_network_idle: bool, ?print_options: ::Hash[untyped, untyped], ?remote_browser_url: untyped?, ?network_log_format: ::Symbol, ?chrome_args: untyped) -> void
|
|
53
|
+
def initialize: (url: untyped, inputfile: untyped, output: untyped, cookies: untyped, headers: untyped, auth: untyped, ?headless: bool, ?port: ::Integer, ?wait_window_loaded: bool, ?wait_network_idle: bool, ?print_options: ::Hash[untyped, untyped], ?remote_browser_url: untyped?, ?network_log_format: ::Symbol, ?chrome_args: untyped, ?emulate_media: ::Hash[untyped, untyped]) -> void
|
|
69
54
|
|
|
70
55
|
def launch: () -> untyped
|
|
71
56
|
|
data/sig/bidi2pdf/recipe.rbs
CHANGED
|
@@ -10,7 +10,7 @@ module Bidi2pdf
|
|
|
10
10
|
|
|
11
11
|
KNOWN_TOP_LEVEL_KEYS: ::Array["version" | "source" | "browser" | "headers" | "cookies" | "auth" | "actions" | "assert" | "print" | "output"]
|
|
12
12
|
|
|
13
|
-
KNOWN_ACTIONS: ::Array["wait_for" | "click" | "evaluate" | "inject_script" | "inject_style" | "set_viewport" | "wait_network_idle"]
|
|
13
|
+
KNOWN_ACTIONS: ::Array["wait_for" | "click" | "evaluate" | "inject_script" | "inject_style" | "set_viewport" | "wait_network_idle" | "emulate_media"]
|
|
14
14
|
|
|
15
15
|
PAGE_ASSERTIONS: ::Array["selector_exists" | "text_present" | "no_console_errors" | "no_network_failures" | "fonts_loaded"]
|
|
16
16
|
|
data/sig/bidi2pdf/schema.rbs
CHANGED
|
@@ -30,7 +30,7 @@ module Bidi2pdf
|
|
|
30
30
|
|
|
31
31
|
RECIPE_SOURCE: { "description" => "exactly one of url, file, stdin", "oneOf" => ::Array[{ "type" => "object", "required" => ::Array["url"], "properties" => { "url" => { "type" => "string" } }, "additionalProperties" => false } | { "type" => "object", "required" => ::Array["file"], "properties" => { "file" => { "type" => "string" } }, "additionalProperties" => false } | { "type" => "object", "required" => ::Array["stdin"], "properties" => { "stdin" => { "const" => true } }, "additionalProperties" => false }] }
|
|
32
32
|
|
|
33
|
-
RECIPE_ACTIONS: { "description" => "one of the
|
|
33
|
+
RECIPE_ACTIONS: { "description" => "one of the 8 known actions - a thin dispatch over BrowserTab's own methods, see README's Agent and Automation Usage section", "oneOf" => ::Array[{ "type" => "object", "required" => ::Array["wait_for"], "additionalProperties" => false, "properties" => { "wait_for" => { "type" => "object", "description" => "exactly one of selector, paged_js, script; timeout is always optional", "oneOf" => ::Array[{ "type" => "object", "required" => ::Array["selector"], "additionalProperties" => false, "properties" => { "selector" => { "type" => "string" }, "timeout" => { "type" => "number" } } } | { "type" => "object", "required" => ::Array["paged_js"], "additionalProperties" => false, "properties" => { "paged_js" => { "type" => "boolean" }, "timeout" => { "type" => "number" } } } | { "type" => "object", "required" => ::Array["script"], "additionalProperties" => false, "properties" => { "script" => { "type" => "string" }, "timeout" => { "type" => "number" } } }] } } } | { "type" => "object", "required" => ::Array["click"], "additionalProperties" => false, "properties" => { "click" => { "type" => "object", "required" => ::Array["selector"], "additionalProperties" => false, "properties" => { "selector" => { "type" => "string" } } } } } | { "type" => "object", "required" => ::Array["evaluate"], "additionalProperties" => false, "properties" => { "evaluate" => { "type" => "object", "required" => ::Array["script"], "additionalProperties" => false, "properties" => { "script" => { "type" => "string" }, "assign" => { "type" => "string" } } } } } | { "type" => "object", "required" => ::Array["inject_script"], "additionalProperties" => false, "properties" => { "inject_script" => { "type" => "object", "additionalProperties" => false, "properties" => { "url" => { "type" => "string" }, "content" => { "type" => "string" }, "id" => { "type" => "string" } } } } } | { "type" => "object", "required" => ::Array["inject_style"], "additionalProperties" => false, "properties" => { "inject_style" => { "type" => "object", "additionalProperties" => false, "properties" => { "url" => { "type" => "string" }, "content" => { "type" => "string" }, "id" => { "type" => "string" } } } } } | { "type" => "object", "required" => ::Array["set_viewport"], "additionalProperties" => false, "properties" => { "set_viewport" => { "type" => "object", "required" => ::Array["width" | "height"], "additionalProperties" => false, "properties" => { "width" => { "type" => "number" }, "height" => { "type" => "number" }, "device_pixel_ratio" => { "type" => "number" } } } } } | { "type" => "object", "required" => ::Array["wait_network_idle"], "additionalProperties" => false, "properties" => { "wait_network_idle" => { "type" => "object", "additionalProperties" => false, "properties" => { "timeout" => { "type" => "number" } } } } } | { "type" => "object", "required" => ::Array["emulate_media"], "additionalProperties" => false, "properties" => { "emulate_media" => { "type" => "object", "additionalProperties" => false, "description" => "the CSS media type and media features the page is shown and printed with, from here on; media: screen prints the screen styles; on a narrow print page (layout width = page width minus the side margins, 9.525 cm without side margins = 360 CSS px) the mobile layout", "properties" => untyped } } }] }
|
|
34
34
|
|
|
35
35
|
RECIPE_ASSERT: { "description" => "one of the 8 known assertions. The presence-only ones (no_console_errors, no_network_failures, fonts_loaded, pdf_not_blank) never have their value read at run time - Recipe::Runner only checks the key is there - so they must be written as `true`; `false` or any other value is rejected as misleading rather than silently ignored. The last 3 assertions also require the pdf-reader gem at --validate time - a runtime fact this schema cannot express, see the top-level description", "oneOf" => ::Array[{ "type" => "object", "required" => ::Array["selector_exists"], "additionalProperties" => false, "properties" => { "selector_exists" => { "type" => "object", "required" => ::Array["selector"], "additionalProperties" => false, "properties" => { "selector" => { "type" => "string" } } } } } | { "type" => "object", "required" => ::Array["text_present"], "additionalProperties" => false, "properties" => { "text_present" => { "type" => "object", "required" => ::Array["text"], "additionalProperties" => false, "properties" => { "text" => { "type" => "string" } } } } } | { "type" => "object", "required" => ::Array["no_console_errors"], "additionalProperties" => false, "properties" => { "no_console_errors" => { "const" => true } } } | { "type" => "object", "required" => ::Array["no_network_failures"], "additionalProperties" => false, "properties" => { "no_network_failures" => { "const" => true } } } | { "type" => "object", "required" => ::Array["fonts_loaded"], "additionalProperties" => false, "properties" => { "fonts_loaded" => { "const" => true } } } | { "type" => "object", "required" => ::Array["page_count"], "additionalProperties" => false, "properties" => { "page_count" => { "oneOf" => ::Array[{ "type" => "integer" } | { "type" => "object", "properties" => { "min" => { "type" => "integer" }, "max" => { "type" => "integer" } }, "additionalProperties" => false }] } } } | { "type" => "object", "required" => ::Array["pdf_text_present"], "additionalProperties" => false, "properties" => { "pdf_text_present" => { "type" => "object", "required" => ::Array["text"], "additionalProperties" => false, "properties" => { "text" => { "type" => "string" } } } } } | { "type" => "object", "required" => ::Array["pdf_not_blank"], "additionalProperties" => false, "properties" => { "pdf_not_blank" => { "const" => true } } }] }
|
|
36
36
|
|
|
@@ -31,6 +31,8 @@ module Bidi2pdf
|
|
|
31
31
|
# @param [Boolean] wait_network_idle Whether to wait for the network to become idle. Defaults to false.
|
|
32
32
|
# @param [Hash] print_options Options for printing the page. Defaults to an empty hash.
|
|
33
33
|
# @param [Symbol] network_log_format The format for network logs. Defaults to :console.
|
|
34
|
+
# @param [Hash] emulate_media Media type and features for BrowserTab#emulate_media (media:,
|
|
35
|
+
# color_scheme:, reduced_motion:), applied before navigating. Defaults to none.
|
|
34
36
|
class SessionRunner
|
|
35
37
|
@session: untyped
|
|
36
38
|
|
|
@@ -54,6 +56,8 @@ module Bidi2pdf
|
|
|
54
56
|
|
|
55
57
|
@network_log_format: untyped
|
|
56
58
|
|
|
59
|
+
@emulate_media: untyped
|
|
60
|
+
|
|
57
61
|
@window: untyped
|
|
58
62
|
|
|
59
63
|
@tab: untyped
|
|
@@ -63,7 +67,7 @@ module Bidi2pdf
|
|
|
63
67
|
@uri: untyped
|
|
64
68
|
|
|
65
69
|
# rubocop: disable Metrics/ParameterLists
|
|
66
|
-
def initialize: (session: untyped, url: untyped, inputfile: untyped, output: untyped, ?cookies: ::Hash[untyped, untyped], ?headers: ::Hash[untyped, untyped], ?auth: ::Hash[untyped, untyped], ?wait_window_loaded: bool, ?wait_network_idle: bool, ?print_options: ::Hash[untyped, untyped], ?network_log_format: ::Symbol) -> void
|
|
70
|
+
def initialize: (session: untyped, url: untyped, inputfile: untyped, output: untyped, ?cookies: ::Hash[untyped, untyped], ?headers: ::Hash[untyped, untyped], ?auth: ::Hash[untyped, untyped], ?wait_window_loaded: bool, ?wait_network_idle: bool, ?print_options: ::Hash[untyped, untyped], ?network_log_format: ::Symbol, ?emulate_media: ::Hash[untyped, untyped]) -> void
|
|
67
71
|
|
|
68
72
|
def run: () -> untyped
|
|
69
73
|
|
data/sig/bidi2pdf/version.rbs
CHANGED